Compare commits

..

33 Commits

Author SHA1 Message Date
yeongpin
ad98bed98d Refactor: Remove GitHub trial reset feature and add development version check. Update translations for lifetime access and development version messages. 2025-03-16 10:54:43 +08:00
Pin Studios
6f2ec1b373 Merge pull request #250 from yeongpin/revert-248-feature/github-reset-trial
Revert "feat: Add GitHub-based trial reset"
2025-03-16 10:45:52 +08:00
Pin Studios
5574091273 Revert "feat: Add GitHub-based trial reset" 2025-03-16 10:44:40 +08:00
Pin Studios
0226c9e735 Update CHANGELOG.md 2025-03-16 10:18:35 +08:00
Pin Studios
375347a7a5 Update .env 2025-03-16 10:05:57 +08:00
Pin Studios
8301ea74d5 Delete pr_description.md 2025-03-16 10:05:41 +08:00
Pin Studios
96981a2c37 Merge pull request #248 from Nigel1992/feature/github-reset-trial
feat: Add GitHub-based trial reset
2025-03-16 10:04:44 +08:00
Nigel1992
975647765d feat: Add JavaScript trial reset code and update PR description 2025-03-15 23:52:39 +01:00
Nigel1992
808b1ba3dc feat: Add JavaScript trial reset code for automatic account deletion 2025-03-15 23:48:55 +01:00
Nigel1992
9595a8792f docs: Update PR description to include JavaScript trial reset code 2025-03-15 23:45:15 +01:00
Nigel1992
6b5dfab362 feat: Add GitHub-based trial reset with improved organization 2025-03-15 23:39:02 +01:00
Pin Studios
d5fceb2624 Update .env 2025-03-15 23:46:50 +08:00
Pin Studios
45046002df Merge pull request #243 from mALIk-sHAHId/feature/google-github-auth
feat: Add Google and GitHub OAuth Authentication with Lifetime Access…
2025-03-15 23:46:28 +08:00
mALIk-sHAHId
6bb33cec4c feat: Add Google and GitHub OAuth Authentication with Lifetime Access - Add OAuth integrations, update menu system, add multi-language support, and update documentation 2025-03-15 19:39:30 +05:30
Pin Studios
66f6197e6d Update block_domain.txt 2025-03-15 11:10:30 +08:00
Pin Studios
5514e47759 Update block_domain.txt 2025-03-15 10:29:03 +08:00
Pin Studios
3a53d59d52 Merge pull request #227 from distributedStorage/enhance-readme
doc: unify the README & CHANGELOG style
2025-03-14 23:02:31 +08:00
imbajin
c2e5499f19 doc: unify the README & CHANGELOG 2025-03-14 15:29:49 +08:00
Pin Studios
9a223756c5 Merge pull request #226 from distributedStorage/fix-download
fix(script): avoid empty version when call release api
2025-03-14 15:26:39 +08:00
imbajin
9b5912357d fix(script): avoid empty version when call release api
Also enhance the way for judging $?
2025-03-14 15:11:57 +08:00
Pin Studios
3200271156 Merge pull request #224 from RenjiYuusei/main
Add Vietnamese Language Support
2025-03-14 12:15:53 +08:00
yeongpin
8401f4718e Update CHANGELOG.md for version 1.7.07
- Add Vietnamese language support and admin privilege management features for Windows executables.
- Implement admin privilege detection and enhance startup process with verification.
- Introduce functions to check and request admin rights when running as a frozen executable.
- Include new admin-related emoji in the EMOJI dictionary and provide fallback for non-Windows platforms.
2025-03-14 12:15:37 +08:00
yeongpin
b761bf0b6d Update localization and versioning for Vietnamese support
- Increment version to 1.7.07 in .env file.
- Add Vietnamese language support in main.py and localization files (vi.json, zh_cn.json, zh_tw.json).
- Translate Chinese language options to English in localization files.
2025-03-14 12:12:58 +08:00
Renji Yuusei
db95689a8e Remove Vietnamese Language Option from Chinese Localization Files
- Updated zh_cn.json and zh_tw.json to remove the Vietnamese language entry while retaining other language options.
2025-03-14 06:54:12 +07:00
Renji Yuusei
652ffc809a Add Vietnamese Language Support
- Update localization files to include Vietnamese language option
2025-03-14 00:25:20 +07:00
Pin Studios
a854969682 Update README.md 2025-03-13 12:09:56 +08:00
Pin Studios
9c5ac85759 Merge pull request #199 from ahmed98Osama/feature/admin-privileges
feat: Add Admin Privilege Management for Windows Executable
2025-03-13 12:08:36 +08:00
Ahemd Farouk
a67264d5c2 refactor: Simplify Admin Privilege Request Logic
- Consolidate admin privilege request logic for Windows by removing redundant checks for frozen executables
2025-03-12 16:51:56 +02:00
Ahemd Farouk
68b1dae466 feat: Add Admin Privilege Management for Windows Executable
- Implement admin privilege detection for Windows platform
- Add functions to check and request admin rights when running as a frozen executable
- Enhance startup process with admin privilege verification
- Add new admin-related emoji to the EMOJI dictionary
- Provide fallback mechanism for non-Windows platforms
2025-03-11 14:56:47 +02:00
yeongpin
0da6f9a1b7 feat: Add Chinese and English Question Issue Templates
- Create bilingual issue templates for discussions and questions
- Add comprehensive sections for platform, version, and problem description
- Include checklist to guide users in providing clear and helpful issue details
- Support both Chinese and English language issue submissions
2025-03-11 12:53:58 +08:00
yeongpin
59fccecb0f feat: Add English Bug Report Issue Template
- Create a new issue template for English-language bug reports
- Mirror the structure of the Chinese bug report template
- Include comprehensive sections for platform, version, and error description
- Add checklist to guide users in providing clear and helpful issue details
2025-03-11 12:47:33 +08:00
yeongpin
1769d245f9 feat: Add Chinese Bug Report Issue Template
- Create a new issue template for Chinese-language bug reports
- Include comprehensive sections for platform, version, and error description
- Add checklist to guide users in providing clear and helpful issue details
- Enhance issue submission process with specific guidelines
2025-03-11 12:45:17 +08:00
yeongpin
b98f094407 refactor: Internationalize and Clean Up Code Comments
- Translate Chinese comments to English across multiple files
- Improve code readability by using consistent, clear comments
- Remove redundant comments and simplify language-specific annotations
- Maintain existing code structure while enhancing internationalization
2025-03-11 12:09:07 +08:00
25 changed files with 1830 additions and 255 deletions

4
.env
View File

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

View File

@@ -0,0 +1,70 @@
name: ❌ 错误报告 [中文]
description: 创建一个报告以帮助我们改进
title: '[Bug]: '
labels: ['bug']
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
在提交 Issue 前请确保您已经阅读了[Github Issues](https://github.com/yeongpin/cursor-free-vip/issues)
- type: checkboxes
id: checklist
attributes:
label: 提交前检查
description: |
请确保您在提交 Issue 前已经完成了以下所有步骤
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决。
required: true
- label: 我已经查看了置顶 Issue 并搜索了现有的 [开放 Issue](https://github.com/yeongpin/cursor-free-vip/issues)和[已关闭 Issue](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20),没有找到类似的问题。
required: true
- label: 我填写了简短且清晰明确的标题,以便开发者在翻阅 Issue 列表时能快速确定大致问题。而不是“一个建议”、“卡住了”等。
required: true
- type: dropdown
id: platform
attributes:
label: 平台
description: 您正在使用哪个平台?
options:
- Windows x32
- Windows x64
- macOS Intel
- macOS ARM64
- Linux x64
- Linux ARM64
validations:
required: true
- type: input
id: version
attributes:
label: 版本
description: 您正在运行的 Cursor Free Vip 版本是什么?
placeholder: 例如 v1.0.0
validations:
required: true
- type: textarea
id: description
attributes:
label: 错误描述
description: 描述问题时请尽可能详细
placeholder: 告诉我们发生了什么...
validations:
required: true
- type: textarea
id: logs
attributes:
label: 相关日志输出
description: 请复制并粘贴任何相关的日志输出
render: shell
- type: textarea
id: additional
attributes:
label: 附加信息
description: 任何能让我们对你所遇到的问题有更多了解的东西

75
.github/ISSUE_TEMPLATE/cn_question.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
name: ❓ 讨论 & 提问 (中文)
description: 寻求帮助、讨论问题、提出疑问等...
title: '[讨论]: '
labels: ['question']
body:
- type: markdown
attributes:
value: |
感谢您的提问!请尽可能详细地描述您的问题,这样我们才能更好地帮助您。
- type: checkboxes
id: checklist
attributes:
label: Issue 检查清单
description: |
在提交 Issue 前请确保您已经完成了以下所有步骤
options:
- label: 我理解 Issue 是用于反馈和解决问题的,而非吐槽评论区,将尽可能提供更多信息帮助问题解决。
required: true
- label: 我确认自己需要的是提出问题并且讨论问题,而不是 Bug 反馈或需求建议。
required: true
- label: 我已阅读 [Github Issues](https://github.com/yeongpin/cursor-free-vip/issues) 并搜索了现有的 [开放 Issue](https://github.com/yeongpin/cursor-free-vip/issues) 和 [已关闭 Issue](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20),没有找到类似的问题。
required: true
- type: dropdown
id: platform
attributes:
label: 平台
description: 您正在使用哪个平台?
options:
- Windows x32
- Windows x64
- macOS Intel
- macOS ARM64
- Linux x64
- Linux ARM64
validations:
required: true
- type: input
id: version
attributes:
label: 版本
description: 您正在运行的 Cursor Free Vip 版本是什么?
placeholder: 例如 v1.0.0
validations:
required: true
- type: textarea
id: question
attributes:
label: 您的问题
description: 请详细描述您的问题
placeholder: 请尽可能清楚地说明您的问题...
validations:
required: true
- type: textarea
id: additional
attributes:
label: 补充信息
description: 任何其他相关的信息、截图或代码示例
render: shell
- type: dropdown
id: priority
attributes:
label: 优先级
description: 这个问题对您来说有多紧急?
options:
- 低 (有空再看)
- 中 (希望尽快得到答复)
- 高 (阻碍工作进行)
validations:
required: true

View File

@@ -0,0 +1,70 @@
name: ❌ Bug Report [English]
description: Create a report to help us improve
title: '[Bug]: '
labels: ['bug']
body:
- type: markdown
attributes:
value: |
Thank you for taking the time to fill out this bug report!
Before submitting this issue, please ensure that you have read the [github issues](https://github.com/yeongpin/cursor-free-vip/issues)
- type: checkboxes
id: checklist
attributes:
label: Commit before submitting
description: |
Please ensure that you have completed all of the following steps before submitting an issue
options:
- label: I understand that Issues are used to provide feedback and solve problems, not to complain in the comments section, and will provide more information to help solve the problem.
required: true
- label: I have checked the top Issue and searched for existing [open issues](https://github.com/yeongpin/cursor-free-vip/issues) and [closed issues](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20), and found no similar issues.
required: true
- label: I have filled out a short and clear title, so that developers can quickly determine the general problem when browsing the Issue list. Not "a suggestion", "stuck", etc.
required: true
- type: dropdown
id: platform
attributes:
label: Platform
description: Which platform are you using?
options:
- Windows x32
- Windows x64
- macOS Intel
- macOS ARM64
- Linux x64
- Linux ARM64
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: What version of Cursor Free Vip are you running?
placeholder: For example v1.0.0
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: Please describe the problem as detailed as possible
placeholder: Tell us what happened...
validations:
required: true
- type: textarea
id: logs
attributes:
label: Related log output
description: Please copy and paste any related log output
render: shell
- type: textarea
id: additional
attributes:
label: Additional information
description: Anything that might help us understand the problem better

75
.github/ISSUE_TEMPLATE/en_question.yml vendored Normal file
View File

@@ -0,0 +1,75 @@
name: ❓ Discussion & Question [English]
description: Seeking help, discussing problems, asking questions, etc.
title: '[Discussion]: '
labels: ['question']
body:
- type: markdown
attributes:
value: |
Thank you for your question! Please describe your problem as detailed as possible so we can help you better.
- type: checkboxes
id: checklist
attributes:
label: Issue Checklist
description: |
Please ensure that you have completed all of the following steps before submitting an issue
options:
- label: I understand that Issues are used to provide feedback and solve problems, not to complain in the comments section, and will provide more information to help solve the problem.
required: true
- label: I confirm that I need to raise questions and discuss problems, not Bug feedback or demand suggestions.
required: true
- label: I have read [Github Issues](https://github.com/yeongpin/cursor-free-vip/issues) and searched for existing [open issues](https://github.com/yeongpin/cursor-free-vip/issues) and [closed issues](https://github.com/yeongpin/cursor-free-vip/issues?q=is%3Aissue%20state%3Aclosed%20), and found no similar issues.
required: true
- type: dropdown
id: platform
attributes:
label: Platform
description: Which platform are you using?
options:
- Windows x32
- Windows x64
- macOS Intel
- macOS ARM64
- Linux x64
- Linux ARM64
validations:
required: true
- type: input
id: version
attributes:
label: Version
description: What version of Cursor Free Vip are you running?
placeholder: For example v1.0.0
validations:
required: true
- type: textarea
id: question
attributes:
label: Your question
description: Please describe your problem as detailed as possible
placeholder: Please explain your question as clearly as possible...
validations:
required: true
- type: textarea
id: additional
attributes:
label: Additional information
description: Any other related information, screenshots, or code examples
render: shell
- type: dropdown
id: priority
attributes:
label: Priority
description: How urgent is this issue for you?
options:
- Low (I'll look at it when I have time)
- Medium (I hope to get an answer soon)
- High (It blocks my work)
validations:
required: true

View File

@@ -1,21 +1,44 @@
# Change Log
## v1.7.09
1. Add: Development Version Check | 增加開發版本檢查
2. Remove: Github Trial Reset | 移除 Github 試用重置
3. Fixed: Some Issues | 修復一些問題
## v1.7.08
1. Add: Google OAuth Authentication | 增加 Google OAuth 認證
2. Add: GitHub OAuth Authentication | 增加 GitHub OAuth 認證
3. Add: Lifetime Access for OAuth Users | 增加 OAuth 用戶終身訪問權限
4. Add: OAuth Authentication Integration | 增加 OAuth 認證集成
5. Update: Menu System with OAuth Options | 更新菜單系統,添加 OAuth 選項
6. Add: Multi-language Support for OAuth | 增加 OAuth 多語言支持
## v1.7.07
1. Add: Vietnamese Language | 增加越南語言
2. Add: Admin Privilege Management for Windows Executable | 增加 Windows 可執行文件管理員權限
3. Implement admin privilege detection for Windows platform | 實現 Windows 平台管理員權限檢測
4. Add functions to check and request admin rights when running as a frozen executable | 增加檢查和請求管理員權限的功能
5. Enhance startup process with admin privilege verification | 增強啟動過程中的管理員權限驗證
6. Add new admin-related emoji to the EMOJI dictionary | 增加新的管理員相關表情符號到 EMOJI 字典
7. Provide fallback mechanism for non-Windows platforms (macos and linux ) | 提供非 Windows 平台macos 和 linux的回退機制
These changes make the application more user-friendly by only requesting admin privileges when necessary (when running as an executable). | 這些改進使應用程序更易於使用,只在必要時(當作為可執行文件運行時)請求管理員權限。
## v1.7.06
1. Add: Update Confirm | 增加更新確認
2. Add: Update Skipped | 增加更新跳過
3. Add: Invalid Choice | 增加無效選擇
4. Fix: Cursor Path | 修復Cursor路徑
4. Fix: Cursor Path | 修復 Cursor 路徑
5. Fix: Path Encoding | 修復路徑編碼
6. Fix: Getting Verification Code | 修復獲取驗證碼
7. Fix: Setting Password | 修復設置密碼
8. Fix: Disable Auto Update | 修復禁用自動更新
9. Add Config.py | 增加Config.py
10. Add utils.py | 增加utils.py
9. Add Config.py | 增加 Config.py
10. Add utils.py | 增加 utils.py
11. Rebuild some logic | 重新構建一些邏輯
## v1.7.05
1. Fix: Cursor Version Check | 修復Cursor版本檢查
1. Fix: Cursor Version Check | 修復 Cursor 版本檢查
2. Fix: Small Problem | 修復一些小問題
@@ -26,10 +49,10 @@
1. Hotfix: Small Problem | 修復一些小問題
## v1.7.02
1. Fix: Cursor Path | 修復Cursor路徑
1. Fix: Cursor Path | 修復 Cursor 路徑
2. Add: Config File | 增加配置文件
3. Remove: Workbench Cursor Path | 移除Workbench Cursor路徑
4. Remove: Cursor Main JS | 移除Cursor main.js
3. Remove: Workbench Cursor Path | 移除 Workbench Cursor 路徑
4. Remove: Cursor Main JS | 移除 Cursor main.js
## v1.7.01
- Refactoring: Extract configuration-related code from the `setup_driver` function to an independent `setup_config` function
@@ -44,10 +67,10 @@
2. Add: Test some Bypass Code | 測試一些繞過代碼
## v1.6.01
1. Fix: Cursor Auth | 修復Cursor Auth
1. Fix: Cursor Auth | 修復 Cursor Auth
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
3. Fix: Cursor Auth Error | 修復Cursor Auth錯誤
4. Fix: Update Curl Faild | 修復更新Curl失敗
3. Fix: Cursor Auth Error | 修復 Cursor Auth 錯誤
4. Fix: Update Curl Faild | 修復更新 Curl 失敗
## v1.5.03
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
@@ -70,8 +93,8 @@
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調用以再次顯示菜單
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
@@ -86,7 +109,7 @@
## v1.4.05
1. Fix: macOS Language Detection | 修復macOS語言檢測
1. Fix: macOS Language Detection | 修復 macOS 語言檢測
## v1.4.04
@@ -98,46 +121,46 @@
## v1.4.03
1. Switch to API-based Registration System | 改用API註冊系統替代瀏覽器操作
2. Add Support for Latest Cursor Version | 增加支持最新版本Cursor
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集成
6. Add New API Integration | 新增 API 集成
7. Optimize Performance and Stability | 優化性能和穩定性
## v1.4.01
1. Add Disable Cursor Auto Upgrade | 增加禁用Cursor自動升級
1. Add Disable Cursor Auto Upgrade | 增加禁用 Cursor 自動升級
## v1.3.02
1. Add Buy Me a Coffee | 增加請我喝杯咖啡
2. Add PayPal | 增加PayPal
2. Add PayPal | 增加 PayPal
3. Very Small Fix | 非常小的修復
4. Fix main.py option number | 修復main.py選項數量
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選項
3. Fix Cursor Options | 修復 Cursor 選項
## v1.2.02
1. Add PBlock | 增加PBlock
2. Remove uBlock0.chromium | 移除uBlock0.chromium
1. Add PBlock | 增加 PBlock
2. Remove uBlock0.chromium | 移除 uBlock0.chromium
3. Optimize the logic of the script | 優化腳本邏輯
4. Optimize Size | 優化大小
## v1.2.01
1. Fix Cursor Cloudflare Human Verification Problem | 修復Cursor Cloudflare人機驗證問題
1. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題
2. Change to automatic registration account | 全面改為自動註冊賬號
3. Change Mail Site | 改變郵箱網站
4. Fix Cursor Cloudflare Problem | 修復Cursor Cloudflare問題
4. Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題
## v1.1.01
@@ -146,15 +169,15 @@
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
</p>
1. Hot Fix Cursor Cloudflare Problem | 修復Cursor Cloudflare問題
2. Fix Cursor Cloudflare Human Verification Problem | 修復Cursor Cloudflare人機驗證問題
1. Hot Fix Cursor Cloudflare Problem | 修復 Cursor Cloudflare 問題
2. Fix Cursor Cloudflare Human Verification Problem | 修復 Cursor Cloudflare 人機驗證問題
3. Remake signup logic | 重做註冊邏輯
## v1.0.10
1. Hot Fix Mac Chrome Problem | 修復Mac Chrome問題
2. Fix Linux Start Donet Problem | 修復Linux啟動開發者問題
1. Hot Fix Mac Chrome Problem | 修復 Mac Chrome 問題
2. Fix Linux Start Donet Problem | 修復 Linux 啟動開發者問題
## v1.0.9
@@ -163,15 +186,15 @@
<img src="./images/cloudflare_2025-02-12_13-43-21.png" alt="free" width="400"/><br>
</p>
1. Fixed New 0.45.x Version Reset Machine | 修復新0.45版本重置機器
1. Fixed New 0.45.x Version Reset Machine | 修復新 0.45 版本重置機器
2. Fix Locale Language | 修復多語言
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
4. Add Remake main.js | 重做main.js
4. Add Remake main.js | 重做 main.js
## v1.0.8
1. Fix New 0.45 Version Reset Machine | 修復新0.45版本重置機器
1. Fix New 0.45 Version Reset Machine | 修復新 0.45 版本重置機器
2. Fix Locale Language | 修復多語言
3. Add Support Crypto Machine Regedit | 增加支持加密機器註冊
@@ -192,17 +215,17 @@
## v1.0.6
1. Add Quit Cursor Option | 增加退出Cursor選項
2. Add Recaptcha Path Patch | 增加Recaptcha路徑修復
1. Add Quit Cursor Option | 增加退出 Cursor 選項
2. Add Recaptcha Path Patch | 增加 Recaptcha 路徑修復
3. Fix Admin Permission | 修復管理員權限問題
4. Remove all need admin permission | 移除所有需要管理員權限
## v1.0.5 - HotFix
1. Fix: Mac Browser Control | 修復Mac瀏覽器控制問題
1. Fix: Mac Browser Control | 修復 Mac 瀏覽器控制問題
2. Fix: Verification Code Cant Patch | 修復驗證碼無法修復問題
3. Add Linux Support | 增加Linux支持
3. Add Linux Support | 增加 Linux 支持
<p align="center">
<img src="./images/fix_2025-01-14_21-30-43.png" alt="fix" width="400"/><br>
</p>
@@ -210,7 +233,7 @@
## v1.0.5
1. Remove MachineID | 移除機器碼ID
1. Remove MachineID | 移除機器碼 ID
2. Change to automatic registration account | 全面改為自動註冊賬號
3. Use your own exclusive new account | 使用自己獨享的新賬號
4. Fully automatic reset machine configuration | 全面自動化重置機器配置
@@ -221,16 +244,16 @@
## v1.0.4
1. Fix: Cursor's configuration | 修復Cursor的配置問題
1. Fix: Cursor's configuration | 修復 Cursor 的配置問題
2. Fix Cloud Lame | 修復雲端慢速模式
## v1.0.3
1. Fix: Cursor's configuration | 修復Cursor的配置問題
1. Fix: Cursor's configuration | 修復 Cursor 的配置問題
2. Add Manual Reset Machine | 增加手動重置機器
3. Add CDN Cloud Control WatchDog | 增加CDN雲端控制WatchDog
4. Add Mac OS Support | 增加Mac OS支持
3. Add CDN Cloud Control WatchDog | 增加 CDN 雲端控制 WatchDog
4. Add Mac OS Support | 增加 Mac OS 支持
5. 759 ++ People use , but star only a few | 759 ++人使用,但只有幾個人點贊
<p align="center">
<img src="./images/what_2025-01-13_13-32-54.png" alt="Why" width="400"/><br>
@@ -242,21 +265,21 @@
1. Fix: Some known issues | 修復了一些已知問題
2. Add cloud control device code | 增加雲端控制設備碼
3. Cloud reset device code | 雲端重置設備碼
4. Remove official WatchDog monitoring | 移除官方WatchDog監控
5. Remove Proxy official prompt | 移除Proxy 官方提示
6. Fix: Too Many Computer | 修復Too Many Computer 問題
4. Remove official WatchDog monitoring | 移除官方 WatchDog 監控
5. Remove Proxy official prompt | 移除 Proxy 官方提示
6. Fix: Too Many Computer | 修復 Too Many Computer 問題
7. Fix Billing Issue | 修復計費問題
8. Fix: Cursor's configuration | 修復Cursor的配置問題
9. Fix cursor-slow mode | 修復cursor-slow模式
8. Fix: Cursor's configuration | 修復 Cursor 的配置問題
9. Fix cursor-slow mode | 修復 cursor-slow 模式
## v1.0.1
1. Fix: Reset machine ID | 修復了重置機器ID的問題
1. Fix: Reset machine ID | 修復了重置機器 ID 的問題
2. Fix: Bypass membership check | 修復了 繞過會員檢查的問題
3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為pro會員的問題
4. Fix: Real-time send Token request | 修復了 實時發送Token請求的問題
5. Fix: Reset Cursor's configuration | 修復了 重置Cursor的配置的問題
3. Fix: Auto upgrade to "pro" membership | 修復了 自動升級為 pro 會員的問題
4. Fix: Real-time send Token request | 修復了 實時發送 Token 請求的問題
5. Fix: Reset Cursor's configuration | 修復了 重置 Cursor 的配置的問題
@@ -268,7 +291,7 @@
<p align="center">
<img src="./images/pro_2025-01-11_00-51-07.png" alt="Cursor Pro Logo" width="400"/><br>
</p>
2. Add usage period,but can be contacted by leaving MachineID | 不得已才添加但可以通過留下MachineID 聯繫作者
2. Add usage period,but can be contacted by leaving MachineID | 不得已才添加,但可以通過留下 MachineID 聯繫作者
<br>
<p align="center">

View File

@@ -1,4 +1,5 @@
# ➤ Cursor Free VIP
<div align="center">
<p align="center">
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
@@ -12,45 +13,50 @@
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
</p>
<h4>Support Latest 0.46.10 Version | 支持最新0.46.10版本</h4>
<h4>Support Latest 0.47.x Version | 支持最新 0.47.x 版本</h4>
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
This is a tool to automatically register, support Windows and macOS systems, complete Auth verification, and reset
Cursor's configuration.
這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
<p align="center">
<img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
</p>
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
##### 如果沒有Google Chrome可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
##### 如果沒有 Google Chrome可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
</p>
</div>
## 🔄 Change Log | 更新日志
[Watch Change Log | 查看更新日志](CHANGELOG.md)
## ✨ Features | 功能特點
* Automatically register Cursor membership<br>自動註冊Cursor會員<br>
* 🌟 Google OAuth Authentication with Lifetime Access<br>使用 Google OAuth 認證(終身訪問)<br>
* ⭐ GitHub OAuth Authentication with Lifetime Access<br>使用 GitHub OAuth 認證(終身訪問)<br>
* Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
* Complete Auth verification<br>完成Auth驗證<br>
* Complete Auth verification<br>完成 Auth 驗證<br>
* Reset Cursor's configuration<br>重置Cursor的配置<br>
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
## 💻 System Support | 系統支持
|Windows|x64|✅|macOS|Intel|✅|
|:---:|:---:|:---:|:---:|:---:|:---:|
|Windows|x86|✅|macOS|Apple Silicon|✅|
|Linux|x64|✅|Linux|x86|✅|
|Linux|ARM64|✅|Linux|ARM64|✅|
| Windows | x64 | ✅ | macOS | Intel | ✅ |
|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
| Linux | x64 | ✅ | Linux | x86 | ✅ |
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
## 👀 How to use | 如何使用
@@ -58,28 +64,34 @@ This is a tool to automatically register , support Windows and macOS systems, co
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
**Linux/macOS**
```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
```
**Windows**
```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
```
</details>
<details>
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
**Linux/macOS**
```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
```
**Windows**
```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
```
</details>
2. If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C
@@ -140,6 +152,7 @@ retry_interval = 8-12
# Max Timeout | 最大超時時間
max_timeout = 160
```
</details>
* Use administrator to run the script <br>請使用管理員身份運行腳本
@@ -150,15 +163,11 @@ max_timeout = 160
* Please comply with the relevant software usage terms when using this tool <br>使用本工具時請遵守相關軟件使用條款
## 🚨 Common Issues | 常見問題
|如果遇到權限問題,請確保:| 此腳本以管理員身份運行 |
|:---:|:---:|
|If you encounter permission issues, please ensure: | This script is run with administrator privileges |
| 如果遇到權限問題,請確保: | 此腳本以管理員身份運行 |
|:--------------------------------------------------:|:------------------------------------------------:|
| If you encounter permission issues, please ensure: | This script is run with administrator privileges |
## 🤩 Contribution | 貢獻
@@ -170,12 +179,12 @@ max_timeout = 160
</a>
<br /><br />
## 📩 Disclaimer | 免責聲明
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br>
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 | 請我喝杯咖啡
@@ -202,9 +211,5 @@ This tool is only for learning and research purposes, and any consequences arisi
## 📝 License | 授權
本項目採用 [CC BY-NC-ND 4.0](https://creativecommons.org/licenses/by-nc-nd/4.0/) 授權。
本項目採用 [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.

View File

@@ -1,3 +1,5 @@
oakon.com
famamail.com
2925.com
indigobook.com
teihu.com

View File

@@ -13,7 +13,7 @@ def setup_config(translator=None):
config = configparser.ConfigParser()
# 默認配置
# Default configuration
default_config = {
'Chrome': {
'chromepath': get_default_chrome_path()
@@ -40,7 +40,7 @@ def setup_config(translator=None):
}
}
# 添加系統特定路徑配置
# Add system-specific path configuration
if sys.platform == "win32":
appdata = os.getenv("APPDATA")
localappdata = os.getenv("LOCALAPPDATA", "")
@@ -71,7 +71,7 @@ def setup_config(translator=None):
'updater_path': os.path.expanduser("~/.config/cursor-updater")
}
# 讀取現有配置並合併
# Read existing configuration and merge
if os.path.exists(config_file):
config.read(config_file, encoding='utf-8')
config_modified = False

View File

@@ -4,10 +4,10 @@ import sys
from colorama import Fore, Style, init
from config import get_config
# 初始化colorama
# Initialize colorama
init()
# 定义emoji和颜色常量
# Define emoji and color constants
EMOJI = {
'DB': '🗄️',
'UPDATE': '🔄',
@@ -23,13 +23,13 @@ class CursorAuth:
def __init__(self, translator=None):
self.translator = translator
# 获取配置
# Get configuration
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}")
sys.exit(1)
# 根据操作系统获取路径
# Get path based on operating system
try:
if sys.platform == "win32": # Windows
if not config.has_section('WindowsPaths'):
@@ -50,7 +50,7 @@ class CursorAuth:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform') if self.translator else 'Unsupported platform'}{Style.RESET_ALL}")
sys.exit(1)
# 验证路径是否存在
# Verify if the path exists
if not os.path.exists(os.path.dirname(self.db_path)):
raise FileNotFoundError(f"Database directory not found: {os.path.dirname(self.db_path)}")
@@ -58,12 +58,12 @@ class CursorAuth:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.path_error', error=str(e)) if self.translator else f'Error getting database path: {str(e)}'}{Style.RESET_ALL}")
sys.exit(1)
# 检查数据库文件是否存在
# Check if the database file exists
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
# 检查文件权限
# Check file permissions
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
@@ -78,12 +78,12 @@ class CursorAuth:
def update_auth(self, email=None, access_token=None, refresh_token=None):
conn = None
try:
# 确保目录存在并设置正确权限
# Ensure the directory exists and set the correct permissions
db_dir = os.path.dirname(self.db_path)
if not os.path.exists(db_dir):
os.makedirs(db_dir, mode=0o755, exist_ok=True)
# 如果数据库文件不存在,创建一个新的
# If the database file does not exist, create a new one
if not os.path.exists(self.db_path):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
@@ -98,17 +98,17 @@ class CursorAuth:
os.chmod(self.db_path, 0o644)
conn.close()
# 重新连接数据库
# Reconnect to the database
conn = sqlite3.connect(self.db_path)
print(f"{EMOJI['INFO']} {Fore.GREEN} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
cursor = conn.cursor()
# 增加超时和其他优化设置
# Add timeout and other optimization settings
conn.execute("PRAGMA busy_timeout = 5000")
conn.execute("PRAGMA journal_mode = WAL")
conn.execute("PRAGMA synchronous = NORMAL")
# 设置要更新的键值对
# Set the key-value pairs to update
updates = []
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
@@ -121,11 +121,11 @@ class CursorAuth:
updates.append(("cursorAuth/refreshToken", refresh_token))
# 使用事务来确保数据完整性
# Use transactions to ensure data integrity
cursor.execute("BEGIN TRANSACTION")
try:
for key, value in updates:
# 检查键是否存在
# Check if the key exists
cursor.execute("SELECT COUNT(*) FROM ItemTable WHERE key = ?", (key,))
if cursor.fetchone()[0] == 0:
cursor.execute("""

View File

@@ -42,7 +42,7 @@ class CursorRegistration:
self.signup_tab = None
self.email_tab = None
# 账号信息
# Account information
self.password = self._generate_password()
# Generate first name and last name separately
first_name = random.choice([
@@ -196,17 +196,17 @@ class CursorRegistration:
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}")
# 重置机器ID
# Reset machine ID
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
resetter = MachineIDResetter(self.translator) # 创建实例时传入translator
if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法
resetter = MachineIDResetter(self.translator) # Pass translator when creating instance
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID")
# Save account information to file

View File

@@ -0,0 +1,5 @@
from oauth_auth import main as oauth_main
def main(translator=None):
"""Handle GitHub OAuth registration"""
oauth_main('github', translator)

View File

@@ -0,0 +1,5 @@
from oauth_auth import main as oauth_main
def main(translator=None):
"""Handle Google OAuth registration"""
oauth_main('google', translator)

View File

@@ -26,7 +26,7 @@ class AutoUpdateDisabler:
self.translator = translator
self.system = platform.system()
# 从配置文件获取路径
# Get path from configuration file
config = get_config(translator)
if config:
if self.system == "Windows":
@@ -36,7 +36,7 @@ class AutoUpdateDisabler:
elif self.system == "Linux":
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
else:
# 如果配置加载失败,使用默认路径
# If configuration loading fails, use default paths
self.updater_paths = {
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"),
"Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"),

View File

@@ -4,6 +4,8 @@
"exit": "Exit Program",
"reset": "Reset Machine ID",
"register": "Register New Cursor Account",
"register_google": "Register with Google Account",
"register_github": "Register with GitHub Account",
"register_manual": "Register Cursor with Custom Email",
"quit": "Close Cursor Application",
"select_language": "Change Language",
@@ -12,12 +14,14 @@
"program_terminated": "Program was terminated by user",
"error_occurred": "An error occurred: {error}. Please try again",
"press_enter": "Press Enter to Exit",
"disable_auto_update": "Disable Cursor Auto-Update"
"disable_auto_update": "Disable Cursor Auto-Update",
"lifetime_access_enabled": "LIFETIME ACCESS ENABLED"
},
"languages": {
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文"
"zh_tw": "繁體中文",
"vi": "Vietnamese"
},
"quit_cursor": {
"start": "Start Quitting Cursor",
@@ -275,6 +279,7 @@
"continue_anyway": "Continuing with current version...",
"update_confirm": "Do you want to update to the latest version? (Y/n)",
"update_skipped": "Skipping update.",
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'."
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'.",
"development_version": "Development Version {current} > {latest}"
}
}

283
locales/vi.json Normal file
View File

@@ -0,0 +1,283 @@
{
"menu": {
"title": "Các Tùy Chọn Khả Dụng",
"exit": "Thoát Chương Trình",
"reset": "Đặt Lại ID Máy",
"register": "Đăng Ký Tài Khoản Cursor Mới",
"register_manual": "Đăng Ký Cursor Với Email Tùy Chỉnh",
"quit": "Đóng Ứng Dụng Cursor",
"select_language": "Thay Đổi Ngôn Ngữ",
"input_choice": "Vui lòng nhập lựa chọn của bạn ({choices})",
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập một số từ {choices}",
"program_terminated": "Chương trình đã bị người dùng chấm dứt",
"error_occurred": "Đã xảy ra lỗi: {error}. Vui lòng thử lại",
"press_enter": "Nhấn Enter để Thoát",
"disable_auto_update": "Tắt Tự Động Cập Nhật Cursor",
"lifetime_access_enabled": "LIFETIME ACCESS ENABLED"
},
"languages": {
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文",
"vi": "Tiếng Việt"
},
"quit_cursor": {
"start": "Bắt Đầu Thoát Cursor",
"no_process": "Không Có Tiến Trình Cursor Đang Chạy",
"terminating": "Đang Chấm Dứt Tiến Trình {pid}",
"waiting": "Đang Chờ Tiến Trình Thoát",
"success": "Tất Cả Tiến Trình Cursor Đã Đóng",
"timeout": "Tiến Trình Hết Thời Gian: {pids}",
"error": "Đã Xảy Ra Lỗi: {error}"
},
"reset": {
"title": "Công Cụ Đặt Lại ID Máy Cursor",
"checking": "Đang Kiểm Tra Tệp Cấu Hình",
"not_found": "Không Tìm Thấy Tệp Cấu Hình",
"no_permission": "Không Thể Đọc Hoặc Ghi Tệp Cấu Hình, Vui Lòng Kiểm Tra Quyền Tệp",
"reading": "Đang Đọc Cấu Hình Hiện Tại",
"creating_backup": "Đang Tạo Bản Sao Lưu Cấu Hình",
"backup_exists": "Tệp Sao Lưu Đã Tồn Tại, Bỏ Qua Bước Sao Lưu",
"generating": "Đang Tạo ID Máy Mới",
"saving_json": "Đang Lưu Cấu Hình Mới Vào JSON",
"success": "Đặt Lại ID Máy Thành Công",
"new_id": "ID Máy Mới",
"permission_error": "Lỗi Quyền: {error}",
"run_as_admin": "Vui Lòng Thử Chạy Chương Trình Này Với Quyền Quản Trị",
"process_error": "Lỗi Quá Trình Đặt Lại: {error}",
"updating_sqlite": "Đang Cập Nhật Cơ Sở Dữ Liệu SQLite",
"updating_pair": "Đang Cập Nhật Cặp Khóa-Giá Trị",
"sqlite_success": "Cập Nhật Cơ Sở Dữ Liệu SQLite Thành Công",
"sqlite_error": "Cập Nhật Cơ Sở Dữ Liệu SQLite Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"unsupported_os": "Hệ Điều Hành Không Được Hỗ Trợ: {os}",
"linux_path_not_found": "Không Tìm Thấy Đường Dẫn Linux",
"updating_system_ids": "Đang Cập Nhật ID Hệ Thống",
"system_ids_updated": "Cập Nhật ID Hệ Thống Thành Công",
"system_ids_update_failed": "Cập Nhật ID Hệ Thống Thất Bại: {error}",
"windows_guid_updated": "Cập Nhật GUID Windows Thành Công",
"windows_permission_denied": "Windows Từ Chối Quyền",
"windows_guid_update_failed": "Cập Nhật GUID Windows Thất Bại",
"macos_uuid_updated": "Cập Nhật UUID macOS Thành Công",
"plutil_command_failed": "Lệnh plutil Thất Bại",
"start_patching": "Bắt Đầu Vá getMachineId",
"macos_uuid_update_failed": "Cập Nhật UUID macOS Thất Bại",
"current_version": "Phiên Bản Cursor Hiện Tại: {version}",
"patch_completed": "Vá getMachineId Hoàn Tất",
"patch_failed": "Vá getMachineId Thất Bại: {error}",
"version_check_passed": "Kiểm Tra Phiên Bản Cursor Đã Qua",
"file_modified": "Tệp Đã Được Sửa Đổi",
"version_less_than_0_45": "Phiên Bản Cursor < 0.45.0, Bỏ Qua Vá getMachineId",
"detecting_version": "Đang Phát Hiện Phiên Bản Cursor",
"patching_getmachineid": "Đang Vá getMachineId",
"version_greater_than_0_45": "Phiên Bản Cursor >= 0.45.0, Đang Vá getMachineId",
"permission_denied": "Từ Chối Quyền: {error}",
"backup_created": "Đã Tạo Bản Sao Lưu",
"update_success": "Cập Nhật Thành Công",
"update_failed": "Cập Nhật Thất Bại: {error}",
"windows_machine_guid_updated": "Cập Nhật GUID Máy Windows Thành Công",
"reading_package_json": "Đang Đọc package.json {path}",
"invalid_json_object": "Đối Tượng JSON Không Hợp Lệ",
"no_version_field": "Không Tìm Thấy Trường Phiên Bản Trong package.json",
"version_field_empty": "Trường Phiên Bản Trống",
"invalid_version_format": "Định Dạng Phiên Bản Không Hợp Lệ: {version}",
"found_version": "Đã Tìm Thấy Phiên Bản: {version}",
"version_parse_error": "Lỗi Phân Tích Phiên Bản: {error}",
"package_not_found": "Không Tìm Thấy Package.json: {path}",
"check_version_failed": "Kiểm Tra Phiên Bản Thất Bại: {error}",
"stack_trace": "Dấu Vết Ngăn Xếp",
"version_too_low": "Phiên Bản Cursor Quá Thấp: {version} < 0.45.0"
},
"register": {
"title": "Công Cụ Đăng Ký Cursor",
"start": "Bắt đầu quá trình đăng ký...",
"handling_turnstile": "Đang xử lý xác minh bảo mật...",
"retry_verification": "Đang thử lại xác minh...",
"detect_turnstile": "Đang kiểm tra xác minh bảo mật...",
"verification_success": "Xác minh bảo mật thành công",
"starting_browser": "Đang mở trình duyệt...",
"form_success": "Biểu mẫu đã được gửi thành công",
"browser_started": "Trình duyệt đã được mở thành công",
"waiting_for_second_verification": "Đang chờ xác minh email...",
"waiting_for_verification_code": "Đang chờ mã xác minh...",
"password_success": "Đặt mật khẩu thành công",
"password_error": "Không thể đặt mật khẩu: {error}. Vui lòng thử lại",
"waiting_for_page_load": "Đang tải trang...",
"first_verification_passed": "Xác minh ban đầu thành công",
"mailbox": "Đã truy cập hộp thư đến thành công",
"register_start": "Bắt Đầu Đăng Ký",
"form_submitted": "Biểu Mẫu Đã Gửi, Bắt Đầu Xác Minh...",
"filling_form": "Điền Biểu Mẫu",
"visiting_url": "Đang Truy Cập URL",
"basic_info": "Thông Tin Cơ Bản Đã Gửi",
"handle_turnstile": "Xử Lý Turnstile",
"no_turnstile": "Không Phát Hiện Turnstile",
"turnstile_passed": "Đã Vượt Qua Turnstile",
"verification_start": "Bắt Đầu Lấy Mã Xác Minh",
"verification_timeout": "Lấy Mã Xác Minh Hết Thời Gian",
"verification_not_found": "Không Tìm Thấy Mã Xác Minh",
"try_get_code": "Thử | {attempt} Lấy Mã Xác Minh | Thời Gian Còn Lại: {time}s",
"get_account": "Đang Lấy Thông Tin Tài Khoản",
"get_token": "Lấy Token Phiên Cursor",
"token_success": "Lấy Token Thành Công",
"token_attempt": "Thử | {attempt} lần để lấy Token | Sẽ thử lại sau {time}s",
"token_max_attempts": "Đạt Số Lần Thử Tối Đa ({max}) | Không Thể Lấy Token",
"token_failed": "Lấy Token Thất Bại: {error}",
"account_error": "Lấy Thông Tin Tài Khoản Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"browser_start": "Đang Khởi Động Trình Duyệt",
"open_mailbox": "Đang Mở Trang Hộp Thư",
"email_error": "Không Thể Lấy Địa Chỉ Email",
"setup_error": "Lỗi Thiết Lập Email: {error}",
"start_getting_verification_code": "Bắt Đầu Lấy Mã Xác Minh, Sẽ Thử Trong 60s",
"get_verification_code_timeout": "Lấy Mã Xác Minh Hết Thời Gian",
"get_verification_code_success": "Lấy Mã Xác Minh Thành Công",
"try_get_verification_code": "Thử | {attempt} Lấy Mã Xác Minh | Thời Gian Còn Lại: {remaining_time}s",
"verification_code_filled": "Đã Điền Mã Xác Minh",
"login_success_and_jump_to_settings_page": "Đăng Nhập Thành Công và Chuyển Đến Trang Cài Đặt",
"detect_login_page": "Phát Hiện Trang Đăng Nhập, Bắt Đầu Đăng Nhập...",
"cursor_registration_completed": "Đăng Ký Cursor Hoàn Tất!",
"set_password": "Đặt Mật Khẩu",
"basic_info_submitted": "Thông Tin Cơ Bản Đã Gửi",
"cursor_auth_info_updated": "Thông Tin Xác Thực Cursor Đã Cập Nhật",
"cursor_auth_info_update_failed": "Cập Nhật Thông Tin Xác Thực Cursor Thất Bại",
"reset_machine_id": "Đặt Lại ID Máy",
"account_info_saved": "Thông Tin Tài Khoản Đã Lưu",
"save_account_info_failed": "Lưu Thông Tin Tài Khoản Thất Bại",
"get_email_address": "Lấy Địa Chỉ Email",
"update_cursor_auth_info": "Cập Nhật Thông Tin Xác Thực Cursor",
"register_process_error": "Lỗi Quá Trình Đăng Ký: {error}",
"setting_password": "Đang Đặt Mật Khẩu",
"manual_code_input": "Nhập Mã Thủ Công",
"manual_email_input": "Nhập Email Thủ Công",
"password": "Mật Khẩu",
"first_name": "Tên",
"last_name": "Họ",
"exit_signal": "Tín Hiệu Thoát",
"email_address": "Địa Chỉ Email",
"config_created": "Đã Tạo Cấu Hình",
"verification_failed": "Xác Minh Thất Bại",
"verification_error": "Lỗi Xác Minh: {error}",
"config_option_added": "Đã Thêm Tùy Chọn Cấu Hình: {option}",
"config_updated": "Đã Cập Nhật Cấu Hình",
"password_submitted": "Đã Gửi Mật Khẩu",
"total_usage": "Tổng Sử Dụng: {usage}",
"setting_on_password": "Đang Đặt Mật Khẩu",
"getting_code": "Đang Lấy Mã Xác Minh, Sẽ Thử Trong 60s"
},
"auth": {
"title": "Trình Quản Lý Xác Thực Cursor",
"checking_auth": "Đang Kiểm Tra Tệp Xác Thực",
"auth_not_found": "Không Tìm Thấy Tệp Xác Thực",
"auth_file_error": "Lỗi Tệp Xác Thực: {error}",
"reading_auth": "Đang Đọc Tệp Xác Thực",
"updating_auth": "Đang Cập Nhật Thông Tin Xác Thực",
"auth_updated": "Cập Nhật Thông Tin Xác Thực Thành Công",
"auth_update_failed": "Cập Nhật Thông Tin Xác Thực Thất Bại: {error}",
"auth_file_created": "Đã Tạo Tệp Xác Thực",
"auth_file_create_failed": "Tạo Tệp Xác Thực Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"reset_machine_id": "Đặt Lại ID Máy",
"database_connection_closed": "Kết Nối Cơ Sở Dữ Liệu Đã Đóng",
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
"connected_to_database": "Đã Kết Nối Đến Cơ Sở Dữ Liệu",
"updating_pair": "Đang Cập Nhật Cặp Khóa-Giá Trị",
"db_not_found": "Không tìm thấy tệp cơ sở dữ liệu tại: {path}",
"db_permission_error": "Không thể truy cập tệp cơ sở dữ liệu. Vui lòng kiểm tra quyền",
"db_connection_error": "Không thể kết nối đến cơ sở dữ liệu: {error}"
},
"control": {
"generate_email": "Đang Tạo Email Mới",
"blocked_domain": "Tên Miền Bị Chặn",
"select_domain": "Đang Chọn Tên Miền Ngẫu Nhiên",
"copy_email": "Đang Sao Chép Địa Chỉ Email",
"enter_mailbox": "Đang Vào Hộp Thư",
"refresh_mailbox": "Đang Làm Mới Hộp Thư",
"check_verification": "Đang Kiểm Tra Mã Xác Minh",
"verification_found": "Đã Tìm Thấy Mã Xác Minh",
"verification_not_found": "Không Tìm Thấy Mã Xác Minh",
"browser_error": "Lỗi Điều Khiển Trình Duyệt: {error}",
"navigation_error": "Lỗi Điều Hướng: {error}",
"email_copy_error": "Lỗi Sao Chép Email: {error}",
"mailbox_error": "Lỗi Hộp Thư: {error}",
"token_saved_to_file": "Token Đã Lưu Vào cursor_tokens.txt",
"navigate_to": "Đang Điều Hướng Đến {url}",
"generate_email_success": "Tạo Email Thành Công",
"select_email_domain": "Chọn Tên Miền Email",
"select_email_domain_success": "Chọn Tên Miền Email Thành Công",
"get_email_name": "Lấy Tên Email",
"get_email_name_success": "Lấy Tên Email Thành Công",
"get_email_address": "Lấy Địa Chỉ Email",
"get_email_address_success": "Lấy Địa Chỉ Email Thành Công",
"enter_mailbox_success": "Vào Hộp Thư Thành Công",
"found_verification_code": "Đã Tìm Thấy Mã Xác Minh",
"get_cursor_session_token": "Lấy Token Phiên Cursor",
"get_cursor_session_token_success": "Lấy Token Phiên Cursor Thành Công",
"get_cursor_session_token_failed": "Lấy Token Phiên Cursor Thất Bại",
"save_token_failed": "Lưu Token Thất Bại",
"database_updated_successfully": "Cập Nhật Cơ Sở Dữ Liệu Thành Công",
"database_connection_closed": "Kết Nối Cơ Sở Dữ Liệu Đã Đóng",
"no_valid_verification_code": "Không Có Mã Xác Minh Hợp Lệ"
},
"email": {
"starting_browser": "Đang Khởi Động Trình Duyệt",
"visiting_site": "Đang Truy Cập mail.tm",
"create_success": "Tạo Email Thành Công",
"create_failed": "Tạo Email Thất Bại",
"create_error": "Lỗi Tạo Email: {error}",
"refreshing": "Đang Làm Mới Email",
"refresh_success": "Làm Mới Email Thành Công",
"refresh_error": "Lỗi Làm Mới Email: {error}",
"refresh_button_not_found": "Không Tìm Thấy Nút Làm Mới",
"verification_found": "Đã Tìm Thấy Xác Minh",
"verification_not_found": "Không Tìm Thấy Xác Minh",
"verification_error": "Lỗi Xác Minh: {error}",
"verification_code_found": "Đã Tìm Thấy Mã Xác Minh",
"verification_code_not_found": "Không Tìm Thấy Mã Xác Minh",
"verification_code_error": "Lỗi Mã Xác Minh: {error}",
"address": "Địa Chỉ Email",
"all_domains_blocked": "Tất Cả Tên Miền Bị Chặn, Đang Chuyển Dịch Vụ",
"no_available_domains_after_filtering": "Không Có Tên Miền Khả Dụng Sau Khi Lọc",
"switching_service": "Đang Chuyển Sang Dịch Vụ {service}",
"domains_list_error": "Không Thể Lấy Danh Sách Tên Miền: {error}",
"failed_to_get_available_domains": "Không Thể Lấy Tên Miền Khả Dụng",
"domains_excluded": "Tên Miền Bị Loại Trừ: {domains}",
"failed_to_create_account": "Không Thể Tạo Tài Khoản",
"account_creation_error": "Lỗi Tạo Tài Khoản: {error}",
"blocked_domains": "Tên Miền Bị Chặn: {domains}",
"blocked_domains_loaded": "Đã Tải Tên Miền Bị Chặn: {count}",
"blocked_domains_loaded_error": "Lỗi Tải Tên Miền Bị Chặn: {error}",
"blocked_domains_loaded_success": "Tải Tên Miền Bị Chặn Thành Công",
"blocked_domains_loaded_timeout": "Tải Tên Miền Bị Chặn Hết Thời Gian: {timeout}s",
"blocked_domains_loaded_timeout_error": "Lỗi Hết Thời Gian Tải Tên Miền Bị Chặn: {error}",
"available_domains_loaded": "Đã Tải Tên Miền Khả Dụng: {count}",
"domains_filtered": "Tên Miền Đã Lọc: {count}",
"trying_to_create_email": "Đang cố gắng tạo email: {email}"
},
"update": {
"title": "Tắt Tự Động Cập Nhật Cursor",
"disable_success": "Tắt Tự Động Cập Nhật Thành Công",
"disable_failed": "Tắt Tự Động Cập Nhật Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"start_disable": "Bắt Đầu Tắt Tự Động Cập Nhật",
"killing_processes": "Đang Kết Thúc Các Tiến Trình",
"processes_killed": "Đã Kết Thúc Các Tiến Trình",
"removing_directory": "Đang Xóa Thư Mục",
"directory_removed": "Đã Xóa Thư Mục",
"creating_block_file": "Đang Tạo Tệp Chặn",
"block_file_created": "Đã Tạo Tệp Chặn"
},
"updater": {
"checking": "Đang Kiểm Tra Cập Nhật...",
"new_version_available": "Có Phiên Bản Mới! (Hiện Tại: {current}, Mới Nhất: {latest})",
"updating": "Đang Cập Nhật Lên Phiên Bản Mới Nhất. Chương Trình Sẽ Tự Động Khởi Động Lại.",
"up_to_date": "Bạn Đang Sử Dụng Phiên Bản Mới Nhất.",
"check_failed": "Không Thể Kiểm Tra Cập Nhật: {error}",
"continue_anyway": "Tiếp Tục Với Phiên Bản Hiện Tại...",
"update_confirm": "Bạn Có Muốn Cập Nhật Lên Phiên Bản Mới Nhất Không? (Y/n)",
"update_skipped": "Bỏ Qua Cập Nhật.",
"invalid_choice": "Lựa Chọn Không Hợp Lệ. Vui Lòng Nhập 'Y' Hoặc 'n'.",
"development_version": "Phiên Bản Phát Triển {current} > {latest}"
}
}

View File

@@ -4,6 +4,8 @@
"exit": "退出程序",
"reset": "重置机器标识",
"register": "注册新 Cursor 账号",
"register_google": "使用 Google 账号注册",
"register_github": "使用 GitHub 账号注册",
"register_manual": "使用自定义邮箱注册",
"quit": "关闭 Cursor 应用",
"select_language": "更改语言",
@@ -12,12 +14,14 @@
"program_terminated": "程序已被用户终止",
"error_occurred": "发生错误:{error},请重试",
"press_enter": "按回车键退出",
"disable_auto_update": "禁用 Cursor 自动更新"
"disable_auto_update": "禁用 Cursor 自动更新",
"lifetime_access_enabled": "永久订阅"
},
"languages": {
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文"
"zh_tw": "繁體中文",
"vi": "Vietnamese"
},
"quit_cursor": {
"start": "开始退出 Cursor",
@@ -271,6 +275,7 @@
"continue_anyway": "继续使用当前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳过更新。",
"invalid_choice": "选择无效。请输入 'Y' 或 'n'."
"invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
"development_version": "开发版本 {current} > {latest}"
}
}

View File

@@ -12,12 +12,14 @@
"program_terminated": "程式已被使用者終止",
"error_occurred": "發生錯誤:{error},請重試",
"press_enter": "按返回鍵退出",
"disable_auto_update": "停用 Cursor 自動更新"
"disable_auto_update": "停用 Cursor 自動更新",
"lifetime_access_enabled": "永久訂閱"
},
"languages": {
"en": "English",
"zh_cn": "简体中文",
"zh_tw": "繁體中文"
"zh_tw": "繁體中文",
"vi": "Vietnamese"
},
"quit_cursor": {
"start": "開始退出 Cursor",
@@ -253,6 +255,7 @@
"continue_anyway": "繼續使用當前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳過更新。",
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'."
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'.",
"development_version": "開發版本 {current} > {latest}"
}
}

127
main.py
View File

@@ -11,16 +11,16 @@ import requests
import subprocess
from config import get_config
# 只在 Windows 系统上导入 windll
# Only import windll on Windows systems
if platform.system() == 'Windows':
import ctypes
# 只在 Windows 上导入 windll
from ctypes import windll
# 初始化colorama
# Initialize colorama
init()
# 定义emoji和颜色常量
# Define emoji and color constants
EMOJI = {
"FILE": "📄",
"BACKUP": "💾",
@@ -31,13 +31,47 @@ EMOJI = {
"MENU": "📋",
"ARROW": "",
"LANG": "🌐",
"UPDATE": "🔄"
"UPDATE": "🔄",
"ADMIN": "🔐"
}
# Function to check if running as frozen executable
def is_frozen():
"""Check if the script is running as a frozen executable."""
return getattr(sys, 'frozen', False)
# Function to check admin privileges (Windows only)
def is_admin():
"""Check if the script is running with admin privileges (Windows only)."""
if platform.system() == 'Windows':
try:
return ctypes.windll.shell32.IsUserAnAdmin() != 0
except Exception:
return False
# Always return True for non-Windows to avoid changing behavior
return True
# Function to restart with admin privileges
def run_as_admin():
"""Restart the current script with admin privileges (Windows only)."""
if platform.system() != 'Windows':
return False
try:
args = [sys.executable] + sys.argv
# Request elevation via ShellExecute
print(f"{Fore.YELLOW}{EMOJI['ADMIN']} Requesting administrator privileges...{Style.RESET_ALL}")
ctypes.windll.shell32.ShellExecuteW(None, "runas", args[0], " ".join('"' + arg + '"' for arg in args[1:]), None, 1)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to restart with admin privileges: {e}{Style.RESET_ALL}")
return False
class Translator:
def __init__(self):
self.translations = {}
self.current_language = self.detect_system_language() # 使用正确的方法名
self.current_language = self.detect_system_language() # Use correct method name
self.fallback_language = 'en' # Fallback language if translation is missing
self.load_translations()
@@ -58,11 +92,11 @@ class Translator:
def _detect_windows_language(self):
"""Detect language on Windows systems"""
try:
# 确保我们在 Windows
# Ensure we are on Windows
if platform.system() != 'Windows':
return 'en'
# 获取键盘布局
# Get keyboard layout
user32 = ctypes.windll.user32
hwnd = user32.GetForegroundWindow()
threadid = user32.GetWindowThreadProcessId(hwnd, 0)
@@ -73,6 +107,7 @@ class Translator:
0x0409: 'en', # English
0x0404: 'zh_tw', # Traditional Chinese
0x0804: 'zh_cn', # Simplified Chinese
0x0422: 'vi', # Vietnamese
}
return language_map.get(layout_id, 'en')
@@ -96,14 +131,20 @@ class Translator:
return 'zh_cn'
elif system_locale.startswith('en'):
return 'en'
elif system_locale.startswith('vi'):
return 'vi'
# 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'
elif 'vi' in env_lang:
return 'vi'
return 'en'
except:
return 'en'
@@ -168,20 +209,24 @@ class Translator:
"""Get list of available languages"""
return list(self.translations.keys())
# 创建翻译器实例
# Create translator instance
translator = Translator()
def print_menu():
"""打印菜单选项"""
"""Print menu options"""
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
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}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
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.GREEN}3{Style.RESET_ALL}. 🌟 {translator.get('menu.register_google')}")
print(f"{Fore.YELLOW} ┗━━ 🔥 {translator.get('menu.lifetime_access_enabled')} 🔥{Style.RESET_ALL}")
print(f"{Fore.GREEN}4{Style.RESET_ALL}. {translator.get('menu.register_github')}")
print(f"{Fore.YELLOW} ┗━━ 🚀 {translator.get('menu.lifetime_access_enabled')} 🚀{Style.RESET_ALL}")
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}")
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}")
print(f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}")
print(f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}")
print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
def select_language():
@@ -236,10 +281,30 @@ def check_latest_version():
if not latest_version:
raise Exception("Invalid version format received")
if latest_version != version:
# Parse versions for proper comparison
def parse_version(version_str):
"""Parse version string into tuple for proper comparison"""
try:
return tuple(map(int, version_str.split('.')))
except ValueError:
# Fallback to string comparison if parsing fails
return version_str
current_version_tuple = parse_version(version)
latest_version_tuple = parse_version(latest_version)
# Compare versions properly
is_newer_version_available = False
if isinstance(current_version_tuple, tuple) and isinstance(latest_version_tuple, tuple):
is_newer_version_available = current_version_tuple < latest_version_tuple
else:
# Fallback to string comparison
is_newer_version_available = version != latest_version
if is_newer_version_available:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.new_version_available', current=version, latest=latest_version)}{Style.RESET_ALL}")
# 詢問用戶是否要更新
# Ask user if they want to update
while True:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('updater.update_confirm', choices='Y/n')}: {Style.RESET_ALL}").lower()
if choice in ['', 'y', 'yes']:
@@ -283,7 +348,11 @@ def check_latest_version():
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.manual_update_required')}{Style.RESET_ALL}")
return
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
# If current version is newer or equal to latest version
if current_version_tuple > latest_version_tuple:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.development_version', current=version, latest=latest_version)}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
except requests.exceptions.RequestException as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.network_error', error=str(e))}{Style.RESET_ALL}")
@@ -296,9 +365,17 @@ def check_latest_version():
return
def main():
# Check for admin privileges if running as executable on Windows only
if platform.system() == 'Windows' and is_frozen() and not is_admin():
print(f"{Fore.YELLOW}{EMOJI['ADMIN']} Running as executable, administrator privileges required.{Style.RESET_ALL}")
if run_as_admin():
sys.exit(0) # Exit after requesting admin privileges
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Continuing without administrator privileges.{Style.RESET_ALL}")
print_logo()
# 初始化配置
# Initialize configuration
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
@@ -309,7 +386,7 @@ def main():
while True:
try:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-6')}: {Style.RESET_ALL}")
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices='0-8')}: {Style.RESET_ALL}")
if choice == "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
@@ -324,18 +401,26 @@ def main():
cursor_register.main(translator)
print_menu()
elif choice == "3":
import cursor_register_google
cursor_register_google.main(translator)
print_menu()
elif choice == "4":
import cursor_register_github
cursor_register_github.main(translator)
print_menu()
elif choice == "5":
import cursor_register_manual
cursor_register_manual.main(translator)
print_menu()
elif choice == "4":
elif choice == "6":
import quit_cursor
quit_cursor.quit_cursor(translator)
print_menu()
elif choice == "5":
elif choice == "7":
if select_language():
print_menu()
continue
elif choice == "6":
elif choice == "8":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()

View File

@@ -9,12 +9,12 @@ from pathlib import Path
import sys
from config import get_config
# 在文件开头添加全局变量
# Add global variable at the beginning of the file
_translator = None
def cleanup_chrome_processes(translator=None):
"""清理所有Chrome相关进程"""
print("\n正在清理Chrome进程...")
"""Clean all Chrome related processes"""
print("\nCleaning Chrome processes...")
try:
if os.name == 'nt':
os.system('taskkill /F /IM chrome.exe /T 2>nul')
@@ -29,7 +29,7 @@ def cleanup_chrome_processes(translator=None):
print(f"清理进程时出错: {e}")
def signal_handler(signum, frame):
"""处理Ctrl+C信号"""
"""Handle Ctrl+C signal"""
global _translator
if _translator:
print(f"{Fore.CYAN}{_translator.get('register.exit_signal')}{Style.RESET_ALL}")
@@ -39,45 +39,45 @@ def signal_handler(signum, frame):
os._exit(0)
def simulate_human_input(page, url, config, translator=None):
"""访问网址"""
"""Visit URL"""
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
# 先访问空白页面
# First visit blank page
page.get('about:blank')
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 访问目标页面
# Visit target page
page.get(url)
time.sleep(get_random_wait_time(config, 'page_load_wait'))
def fill_signup_form(page, first_name, last_name, email, config, translator=None):
"""填写注册表单"""
"""Fill signup form"""
try:
if translator:
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
else:
print("\n正在填写注册表单...")
# 填写名字
# Fill first name
first_name_input = page.ele("@name=first_name")
if first_name_input:
first_name_input.input(first_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 填写姓氏
# Fill last name
last_name_input = page.ele("@name=last_name")
if last_name_input:
last_name_input.input(last_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 填写邮箱
# Fill email
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 点击提交按钮
# Click submit button
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
@@ -86,14 +86,14 @@ def fill_signup_form(page, first_name, last_name, email, config, translator=None
if translator:
print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}")
else:
print("表单填写完成")
print("Form filled successfully")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"填写表单时出错: {e}")
print(f"Error filling form: {e}")
return False
def get_default_chrome_path():
@@ -143,7 +143,7 @@ def get_random_wait_time(config, timing_type='page_load_wait'):
"""
try:
if not config.has_section('Timing'):
return random.uniform(0.1, 0.8) # 默认值
return random.uniform(0.1, 0.8) # Default value
if timing_type == 'random':
min_time = float(config.get('Timing', 'min_random_time', fallback='0.1'))
@@ -152,20 +152,20 @@ def get_random_wait_time(config, timing_type='page_load_wait'):
time_value = config.get('Timing', timing_type, fallback='0.1-0.8')
# 检查是否为固定时间值
# Check if it's a fixed time value
if '-' not in time_value and ',' not in time_value:
return float(time_value) # 返回固定时间
return float(time_value) # Return fixed time
# 处理范围时间
# Process range time
min_time, max_time = map(float, time_value.split('-' if '-' in time_value else ','))
return random.uniform(min_time, max_time)
except:
return random.uniform(0.1, 0.8) # 出错时返回默认值
return random.uniform(0.1, 0.8) # Return default value when error
def setup_driver(translator=None):
"""Setup browser driver"""
try:
# 获取配置
# Get config
config = get_config(translator)
# Get Chrome path
@@ -185,17 +185,17 @@ def setup_driver(translator=None):
# Use incognito mode
co.set_argument("--incognito")
# 设置随机端口
# Set random port
co.set_argument("--no-sandbox")
# 设置随机端口
# Set random port
co.auto_port()
# 使用有头模式(一定要设置为False模拟人类操作)
# Use headless mode (must be set to False, simulate human operation)
co.headless(False)
try:
# 加载插件
# Load extension
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
if os.path.exists(extension_path):
co.set_argument("--allow-extensions-in-incognito")
@@ -204,12 +204,12 @@ def setup_driver(translator=None):
if translator:
print(f"{Fore.RED}{translator.get('register.extension_load_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"加载插件失败: {e}")
print(f"Error loading extension: {e}")
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.starting_browser')}{Style.RESET_ALL}")
else:
print("正在启动浏览器...")
print("Starting browser...")
page = ChromiumPage(co)
return config, page
@@ -218,26 +218,26 @@ def setup_driver(translator=None):
if translator:
print(f"{Fore.RED}{translator.get('register.browser_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"设置浏览器时出错: {e}")
print(f"Error setting up browser: {e}")
raise
def handle_turnstile(page, config, translator=None):
"""处理 Turnstile 验证"""
"""Handle Turnstile verification"""
try:
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.handling_turnstile')}{Style.RESET_ALL}")
else:
print("\n正在处理 Turnstile 验证...")
print("\nHandling Turnstile verification...")
# from config
turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2'))
random_time_str = config.get('Turnstile', 'handle_turnstile_random_time', fallback='1-3')
# 解析随机时间范围
# Parse random time range
try:
min_time, max_time = map(float, random_time_str.split('-'))
except:
min_time, max_time = 1, 3 # 默认值
min_time, max_time = 1, 3 # Default value
max_retries = 2
retry_count = 0
@@ -247,14 +247,14 @@ def handle_turnstile(page, config, translator=None):
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.retry_verification', attempt=retry_count)}{Style.RESET_ALL}")
else:
print(f" {retry_count} 次尝试验证...")
print(f"Attempt {retry_count} of verification...")
try:
# 尝试重置 turnstile
# Try to reset turnstile
page.run_js("try { turnstile.reset() } catch(e) { }")
time.sleep(turnstile_time) # from config
# 定位验证框元素
# Locate verification box element
challenge_check = (
page.ele("@id=cf-turnstile", timeout=2)
.child()
@@ -267,7 +267,7 @@ def handle_turnstile(page, config, translator=None):
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.detect_turnstile')}{Style.RESET_ALL}")
else:
print("检测到验证框...")
print("Detected verification box...")
# from config
time.sleep(random.uniform(min_time, max_time))
@@ -279,21 +279,21 @@ def handle_turnstile(page, config, translator=None):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("验证通过!")
print("Verification successful!")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print(f"验证尝试失败: {e}")
print(f"Verification attempt failed: {e}")
# 检查是否已经验证成功
# Check if verification has been successful
if check_verification_success(page, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("验证通过!")
print("Verification successful!")
return True
time.sleep(random.uniform(min_time, max_time))
@@ -301,27 +301,27 @@ def handle_turnstile(page, config, translator=None):
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("超出最大重试次数")
print("Exceeded maximum retry attempts")
return False
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"验证过程出错: {e}")
print(f"Error in verification process: {e}")
return False
def check_verification_success(page, translator=None):
"""检查验证是否成功"""
"""Check if verification is successful"""
try:
# 检查是否存在后续表单元素,这表示验证已通过
# Check if there is a subsequent form element, indicating verification has passed
if (page.ele("@name=password", timeout=0.5) or
page.ele("@name=email", timeout=0.5) or
page.ele("@data-index=0", timeout=0.5) or
page.ele("Account Settings", timeout=0.5)):
return True
# 检查是否出现错误消息
# Check if there is an error message
error_messages = [
'xpath://div[contains(text(), "Can\'t verify the user is human")]',
'xpath://div[contains(text(), "Error: 600010")]',
@@ -337,49 +337,49 @@ def check_verification_success(page, translator=None):
return False
def generate_password(length=12):
"""生成随机密码"""
"""Generate random password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def fill_password(page, password: str, config, translator=None):
"""
填写密码表单
Fill password form
"""
try:
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else '设置密码'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else 'Setting password'}{Style.RESET_ALL}")
# 填写密码
# Fill password
password_input = page.ele("@name=password")
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_on_password')}: {password}{Style.RESET_ALL}")
if password_input:
password_input.input(password)
# 点击提交按钮
# Click submit button
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else '密码已提交'}{Style.RESET_ALL}")
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else 'Password submitted'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'设置密码时出错: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'Error setting password: {str(e)}'}{Style.RESET_ALL}")
return False
def handle_verification_code(browser_tab, email_tab, controller, config, translator=None):
"""处理验证码"""
"""Handle verification code"""
try:
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
# 检查是否使用手动输入验证码
if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式
# Check if using manual input verification code
if hasattr(controller, 'get_verification_code') and email_tab is None: # Manual mode
verification_code = controller.get_verification_code()
if verification_code:
# 在注册页面填写验证码
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
@@ -387,13 +387,13 @@ def handle_verification_code(browser_tab, email_tab, controller, config, transla
print(f"{translator.get('register.verification_success')}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 访问设置页面
# Visit settings page
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
@@ -401,20 +401,20 @@ def handle_verification_code(browser_tab, email_tab, controller, config, transla
return False, None
# 自动获取验证码逻辑
# Automatic verification code logic
elif email_tab:
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'email_check_initial_wait'))
# 使用已有的 email_tab 刷新邮箱
# Use existing email_tab to refresh email
email_tab.refresh_inbox()
time.sleep(get_random_wait_time(config, 'email_refresh_wait'))
# 检查邮箱是否有验证码邮件
# Check if there is a verification code email
if email_tab.check_for_cursor_email():
verification_code = email_tab.get_verification_code()
if verification_code:
# 在注册页面填写验证码
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
@@ -423,13 +423,13 @@ def handle_verification_code(browser_tab, email_tab, controller, config, transla
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 访问设置页面
# Visit settings page
if translator:
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
@@ -443,18 +443,18 @@ def handle_verification_code(browser_tab, email_tab, controller, config, transla
print("最后一次验证失败")
return False, None
# 获取验证码,设置超时
# Get verification code, set timeout
verification_code = None
max_attempts = 20
retry_interval = get_random_wait_time(config, 'retry_interval') # 使用 get_random_wait_time
retry_interval = get_random_wait_time(config, 'retry_interval') # Use get_random_wait_time
start_time = time.time()
timeout = float(config.get('Timing', 'max_timeout', fallback='160')) # 這個可以保持不變因為是固定值
timeout = float(config.get('Timing', 'max_timeout', fallback='160')) # This can be kept unchanged because it is a fixed value
if translator:
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
for attempt in range(max_attempts):
# 检查是否超时
# Check if timeout
if time.time() - start_time > timeout:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_timeout')}{Style.RESET_ALL}")
@@ -470,12 +470,12 @@ def handle_verification_code(browser_tab, email_tab, controller, config, transla
if translator:
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
# 刷新邮箱
# Refresh email
email_tab.refresh_inbox()
time.sleep(retry_interval) # 使用 get_random_wait_time
time.sleep(retry_interval) # Use get_random_wait_time
if verification_code:
# 在注册页面填写验证码
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
@@ -484,19 +484,19 @@ def handle_verification_code(browser_tab, email_tab, controller, config, transla
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 直接访问设置页面
# Visit settings page
if translator:
print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
# 直接返回成功,让 cursor_register.py 处理账户信息获取
# Return success directly, let cursor_register.py handle account information acquisition
return True, browser_tab
else:
@@ -512,58 +512,58 @@ def handle_verification_code(browser_tab, email_tab, controller, config, transla
return False, None
def handle_sign_in(browser_tab, email, password, translator=None):
"""处理登录流程"""
"""Handle login process"""
try:
# 检查是否在登录页面
# Check if on login page
sign_in_header = browser_tab.ele('xpath://h1[contains(text(), "Sign in")]')
if not sign_in_header:
return True # 如果不是登录页面,说明已经登录成功
return True # If not on login page, it means login is successful
print(f"{Fore.CYAN}检测到登录页面,开始登录...{Style.RESET_ALL}")
# 填写邮箱
# Fill email
email_input = browser_tab.ele('@name=email')
if email_input:
email_input.input(email)
time.sleep(1)
# 点击 Continue
# Click Continue
continue_button = browser_tab.ele('xpath://button[contains(@class, "BrandedButton") and text()="Continue"]')
if continue_button:
continue_button.click()
time.sleep(2)
# 处理 Turnstile 验证
# Handle Turnstile verification
if handle_turnstile(browser_tab, translator):
# 填写密码
# Fill password
password_input = browser_tab.ele('@name=password')
if password_input:
password_input.input(password)
time.sleep(1)
# 点击 Sign in
# Click Sign in
sign_in_button = browser_tab.ele('xpath://button[@name="intent" and @value="password"]')
if sign_in_button:
sign_in_button.click()
time.sleep(2)
# 处理最后一次 Turnstile 验证
# Handle last Turnstile verification
if handle_turnstile(browser_tab, translator):
print(f"{Fore.GREEN}登录成功!{Style.RESET_ALL}")
print(f"{Fore.GREEN}Login successful!{Style.RESET_ALL}")
time.sleep(3)
return True
print(f"{Fore.RED}登录失败{Style.RESET_ALL}")
print(f"{Fore.RED}Login failed{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}登录过程出错: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}Login process error: {str(e)}{Style.RESET_ALL}")
return False
def main(email=None, password=None, first_name=None, last_name=None, email_tab=None, controller=None, translator=None):
"""主函数,可以接收账号信息、邮箱标签页和翻译器"""
"""Main function, can receive account information, email tab, and translator"""
global _translator
_translator = translator # 保存到全局变量
_translator = translator # Save to global variable
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
@@ -575,45 +575,45 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}")
# 访问注册页面
# Visit registration page
url = "https://authenticator.cursor.sh/sign-up"
# 访问页面
# Visit page
simulate_human_input(page, url, config, translator)
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 如果没有提供账号信息,则生成随机信息
# If account information is not provided, generate random information
if not all([email, password, first_name, last_name]):
first_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
last_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
email = f"{first_name.lower()}{random.randint(100,999)}@example.com"
password = generate_password()
# 保存账号信息
# Save account information
with open('test_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {email}\n")
f.write(f"Password: {password}\n")
f.write(f"{'='*50}\n")
# 填写表单
# Fill form
if fill_signup_form(page, first_name, last_name, email, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}")
# 处理第一次 Turnstile 验证
# Handle first Turnstile verification
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
# 填写密码
# Fill password
if fill_password(page, password, config, translator):
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
# 处理第二次 Turnstile 验证
# Handle second Turnstile verification
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
@@ -621,13 +621,13 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
success = True
return True, page
else:
print(f"\n{Fore.RED}{translator.get('register.verification_code_processing_failed') if translator else '验证码处理失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED}{translator.get('register.verification_code_processing_failed') if translator else 'Verification code processing failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.first_verification_failed') if translator else '第一次验证失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED}{translator.get('register.first_verification_failed') if translator else 'First verification failed'}{Style.RESET_ALL}")
return False, None
@@ -635,7 +635,7 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
print(f"发生错误: {e}")
return False, None
finally:
if page and not success: # 只在失败时清理
if page and not success: # Only clean up when failed
try:
page.quit()
except:
@@ -643,4 +643,4 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
cleanup_chrome_processes(translator)
if __name__ == "__main__":
main() # 直接运行时不传参数,使用随机生成的信息
main() # Run without parameters, use randomly generated information

View File

@@ -263,7 +263,7 @@ class NewTempEmail:
return False
def check_for_cursor_email(self):
"""检查是否有 Cursor 的验证邮件"""
"""Check if there is a Cursor verification email"""
try:
# Use API to get email list
headers = {"Authorization": f"Bearer {self.token}"}

826
oauth_auth.py Normal file
View File

@@ -0,0 +1,826 @@
import os
from colorama import Fore, Style, init
import time
import random
import webbrowser
import sys
import json
from DrissionPage import ChromiumPage, ChromiumOptions
from cursor_auth import CursorAuth
from utils import get_random_wait_time, get_default_chrome_path
from config import get_config
import platform
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'OAUTH': '🔑',
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': ''
}
class OAuthHandler:
def __init__(self, translator=None):
self.translator = translator
self.config = get_config(translator)
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
def _get_active_profile(self, user_data_dir):
"""Find the existing default/active Chrome profile"""
try:
# List all profile directories
profiles = []
for item in os.listdir(user_data_dir):
if item == 'Default' or (item.startswith('Profile ') and os.path.isdir(os.path.join(user_data_dir, item))):
profiles.append(item)
if not profiles:
print(f"{Fore.YELLOW}{EMOJI['INFO']} No Chrome profiles found, using Default{Style.RESET_ALL}")
return 'Default'
# First check if Default profile exists
if 'Default' in profiles:
print(f"{Fore.CYAN}{EMOJI['INFO']} Found Default Chrome profile{Style.RESET_ALL}")
return 'Default'
# If no Default profile, check Local State for last used profile
local_state_path = os.path.join(user_data_dir, 'Local State')
if os.path.exists(local_state_path):
with open(local_state_path, 'r', encoding='utf-8') as f:
local_state = json.load(f)
# Get info about last used profile
profile_info = local_state.get('profile', {})
last_used = profile_info.get('last_used', '')
info_cache = profile_info.get('info_cache', {})
# Try to find an active profile
for profile in profiles:
profile_path = profile.replace('\\', '/')
if profile_path in info_cache:
#print(f"{Fore.CYAN}{EMOJI['INFO']} Using existing Chrome profile: {profile}{Style.RESET_ALL}")
return profile
# If no profile found in Local State, use the first available profile
print(f"{Fore.CYAN}{EMOJI['INFO']} Using first available Chrome profile: {profiles[0]}{Style.RESET_ALL}")
return profiles[0]
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Error finding Chrome profile, using Default: {str(e)}{Style.RESET_ALL}")
return 'Default'
def setup_browser(self):
"""Setup browser for OAuth flow using active profile"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} Initializing browser setup...{Style.RESET_ALL}")
# Platform-specific initialization
platform_name = platform.system().lower()
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected platform: {platform_name}{Style.RESET_ALL}")
# Kill existing browser processes
self._kill_browser_processes()
# Get browser paths and user data directory
user_data_dir = self._get_user_data_directory()
chrome_path = self._get_browser_path()
if not chrome_path:
raise Exception(f"No compatible browser found. Please install Google Chrome or Chromium.\nSupported browsers for {platform_name}:\n" +
"- Windows: Google Chrome, Chromium\n" +
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, chromium-browser")
# Get active profile
active_profile = self._get_active_profile(user_data_dir)
print(f"{Fore.CYAN}{EMOJI['INFO']} Using browser profile: {active_profile}{Style.RESET_ALL}")
# Configure browser options
co = self._configure_browser_options(chrome_path, user_data_dir, active_profile)
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting browser at: {chrome_path}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
# Verify browser launched successfully
if not self.browser:
raise Exception("Failed to initialize browser instance")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Browser setup completed successfully{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Browser setup failed: {str(e)}{Style.RESET_ALL}")
if "DevToolsActivePort file doesn't exist" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} Try running with administrator/root privileges{Style.RESET_ALL}")
elif "Chrome failed to start" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} Make sure Chrome/Chromium is properly installed{Style.RESET_ALL}")
return False
def _kill_browser_processes(self):
"""Kill existing browser processes based on platform"""
try:
if os.name == 'nt': # Windows
processes = ['chrome.exe', 'chromium.exe']
for proc in processes:
os.system(f'taskkill /f /im {proc} >nul 2>&1')
else: # Linux/Mac
processes = ['chrome', 'chromium', 'chromium-browser']
for proc in processes:
os.system(f'pkill -f {proc} >/dev/null 2>&1')
time.sleep(1) # Wait for processes to close
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Warning: Could not kill existing browser processes: {e}{Style.RESET_ALL}")
def _get_user_data_directory(self):
"""Get the appropriate user data directory based on platform"""
try:
if os.name == 'nt': # Windows
possible_paths = [
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data'),
os.path.expandvars(r'%LOCALAPPDATA%\Chromium\User Data')
]
elif sys.platform == 'darwin': # macOS
possible_paths = [
os.path.expanduser('~/Library/Application Support/Google/Chrome'),
os.path.expanduser('~/Library/Application Support/Chromium')
]
else: # Linux
possible_paths = [
os.path.expanduser('~/.config/google-chrome'),
os.path.expanduser('~/.config/chromium'),
'/usr/bin/google-chrome',
'/usr/bin/chromium-browser'
]
# Try each possible path
for path in possible_paths:
if os.path.exists(path):
print(f"{Fore.CYAN}{EMOJI['INFO']} Found browser data directory: {path}{Style.RESET_ALL}")
return path
# Create temporary profile if no existing profile found
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
print(f"{Fore.YELLOW}{EMOJI['INFO']} Creating temporary profile at: {temp_profile}{Style.RESET_ALL}")
os.makedirs(temp_profile, exist_ok=True)
return temp_profile
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting user data directory: {e}{Style.RESET_ALL}")
raise
def _get_browser_path(self):
"""Get the browser executable path based on platform"""
try:
# Try default path first
chrome_path = get_default_chrome_path()
if chrome_path and os.path.exists(chrome_path):
return chrome_path
print(f"{Fore.YELLOW}{EMOJI['INFO']} Searching for alternative browser installations...{Style.RESET_ALL}")
# Platform-specific paths
if os.name == 'nt': # Windows
alt_paths = [
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
r'C:\Program Files\Chromium\Application\chrome.exe',
os.path.expandvars(r'%ProgramFiles%\Google\Chrome\Application\chrome.exe'),
os.path.expandvars(r'%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe')
]
elif sys.platform == 'darwin': # macOS
alt_paths = [
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Chromium.app/Contents/MacOS/Chromium',
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
]
else: # Linux
alt_paths = [
'/usr/bin/google-chrome',
'/usr/bin/chromium-browser',
'/usr/bin/chromium',
'/snap/bin/chromium',
'/usr/local/bin/chrome',
'/usr/local/bin/chromium'
]
# Try each alternative path
for path in alt_paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found browser at: {expanded_path}{Style.RESET_ALL}")
return expanded_path
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error finding browser path: {e}{Style.RESET_ALL}")
return None
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
"""Configure browser options based on platform"""
try:
co = ChromiumOptions()
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
co.set_argument(f'--profile-directory={active_profile}')
# Basic options
co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check')
co.set_argument('--disable-gpu')
# Platform-specific options
if sys.platform.startswith('linux'):
co.set_argument('--no-sandbox')
co.set_argument('--disable-dev-shm-usage')
co.set_argument('--disable-setuid-sandbox')
elif sys.platform == 'darwin':
co.set_argument('--disable-gpu-compositing')
elif os.name == 'nt':
co.set_argument('--disable-features=TranslateUI')
co.set_argument('--disable-features=RendererCodeIntegrity')
return co
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error configuring browser options: {e}{Style.RESET_ALL}")
raise
def handle_google_auth(self):
"""Handle Google OAuth authentication"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start')}{Style.RESET_ALL}")
# Setup browser
if not self.setup_browser():
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
return False, None
# Navigate to auth URL
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Look for Google auth button
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
]
auth_btn = None
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn and auth_btn.is_displayed():
break
except:
continue
if not auth_btn:
raise Exception("Could not find Google authentication button")
# Click the button and wait for page load
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting Google authentication...{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Check if we're on account selection page
if "accounts.google.com" in self.browser.url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account to continue...{Style.RESET_ALL}")
try:
self.browser.run_js("""
alert('Please select your Google account to continue with Cursor authentication');
""")
except:
pass # Alert is optional
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
return False, None
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
return False, None
finally:
try:
if self.browser:
self.browser.quit()
except:
pass
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _wait_for_auth(self):
"""Wait for authentication to complete and extract auth info"""
try:
max_wait = 300 # 5 minutes
start_time = time.time()
check_interval = 2 # Check every 2 seconds
print(f"{Fore.CYAN}{EMOJI['WAIT']} Waiting for authentication (timeout: 5 minutes)...{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
# Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
token = None
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
if token:
# Get email from settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} Authentication successful, getting account info...{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3)
email = None
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {email}{Style.RESET_ALL}")
except:
email = "user@cursor.sh" # Fallback email
# Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150":
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, creating new account...{Style.RESET_ALL}")
# Delete current account
if self._delete_current_account():
# Start new authentication
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new authentication process...{Style.RESET_ALL}")
return self.handle_google_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete expired account{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not check usage count: {str(e)}{Style.RESET_ALL}")
return {"email": email, "token": token}
# Also check URL as backup
if "cursor.com/settings" in self.browser.url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Detected successful login{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Waiting for authentication... ({str(e)}){Style.RESET_ALL}")
time.sleep(check_interval)
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error while waiting for authentication: {str(e)}{Style.RESET_ALL}")
return None
def handle_github_auth(self):
"""Handle GitHub OAuth authentication"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}")
# Setup browser
if not self.setup_browser():
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
return False, None
# Navigate to auth URL
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to authentication page...{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Look for GitHub auth button
selectors = [
"//a[contains(@href,'GitHubOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]",
"(//a[contains(@class,'auth-method-button')])[2]" # Second auth button as fallback
]
auth_btn = None
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn and auth_btn.is_displayed():
break
except:
continue
if not auth_btn:
raise Exception("Could not find GitHub authentication button")
# Click the button and wait for page load
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting GitHub authentication...{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout')}{Style.RESET_ALL}")
return False, None
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication error: {str(e)}{Style.RESET_ALL}")
return False, None
finally:
try:
if self.browser:
self.browser.quit()
except:
pass
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _handle_oauth(self, auth_type):
"""Handle OAuth authentication for both Google and GitHub
Args:
auth_type (str): Type of authentication ('google' or 'github')
"""
try:
if not self.setup_browser():
return False, None
# Navigate to auth URL
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Set selectors based on auth type
if auth_type == "google":
selectors = [
"//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'][contains(@href,'GoogleOAuth')]",
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[1]"
]
else: # github
selectors = [
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[2]"
]
# Wait for the button to be available
auth_btn = None
max_button_wait = 30 # 30 seconds
button_start_time = time.time()
while time.time() - button_start_time < max_button_wait:
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
if auth_btn and auth_btn.is_displayed():
break
except:
continue
if auth_btn:
break
time.sleep(1)
if auth_btn:
# Click the button and wait for page load
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Check if we're on account selection page
if auth_type == "google" and "accounts.google.com" in self.browser.url:
alert_js = """
alert('Please select your Google account manually to continue with Cursor authentication');
"""
try:
self.browser.run_js(alert_js)
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Alert display failed: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Please select your Google account manually to continue with Cursor authentication...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Waiting for authentication to complete...{Style.RESET_ALL}")
# Wait for authentication to complete
max_wait = 300 # 5 minutes
start_time = time.time()
last_url = self.browser.url
print(f"{Fore.CYAN}{EMOJI['WAIT']} Checking authentication status...{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
# Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
if token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful!{Style.RESET_ALL}")
# Navigate to settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} Navigating to settings page...{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3) # Wait for settings page to load
# Get email from settings page
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150": # Changed back to actual condition
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
delete_js = """
function deleteAccount() {
return new Promise((resolve, reject) => {
fetch('https://www.cursor.com/api/dashboard/delete-account', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include'
})
.then(response => {
if (response.status === 200) {
resolve('Account deleted successfully');
} else {
reject('Failed to delete account: ' + response.status);
}
})
.catch(error => {
reject('Error: ' + error);
});
});
}
return deleteAccount();
"""
try:
result = self.browser.run_js(delete_js)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page and repeat authentication
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
# Explicitly navigate to the authentication page
#self.browser.get("https://authenticator.cursor.sh/sign-up")
# time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Call handle_google_auth again to repeat the entire process
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
return self.handle_google_auth()
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
# Also check URL as backup
current_url = self.browser.url
if "cursor.com/settings" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Already on settings page!{Style.RESET_ALL}")
time.sleep(1)
cookies = self.browser.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
if token:
# Get email and check usage here too
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Found email: {actual_email}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find email: {str(e)}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} Usage count: {usage_text}{Style.RESET_ALL}")
# Check if account is expired
if usage_text.strip() == "150 / 150": # Changed back to actual condition
print(f"{Fore.YELLOW}{EMOJI['INFO']} Account has reached maximum usage, deleting...{Style.RESET_ALL}")
delete_js = """
function deleteAccount() {
return new Promise((resolve, reject) => {
fetch('https://www.cursor.com/api/dashboard/delete-account', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include'
})
.then(response => {
if (response.status === 200) {
resolve('Account deleted successfully');
} else {
reject('Failed to delete account: ' + response.status);
}
})
.catch(error => {
reject('Error: ' + error);
});
});
}
return deleteAccount();
"""
try:
result = self.browser.run_js(delete_js)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page and repeat authentication
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting re-authentication process...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirecting to authenticator.cursor.sh...{Style.RESET_ALL}")
# Explicitly navigate to the authentication page
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Call handle_google_auth again to repeat the entire process
print(f"{Fore.CYAN}{EMOJI['INFO']} Starting new Google authentication...{Style.RESET_ALL}")
return self.handle_google_auth()
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to delete account or re-authenticate: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Account is still valid (Usage: {usage_text}){Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Could not find usage count: {str(e)}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
elif current_url != last_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Page changed, checking auth...{Style.RESET_ALL}")
last_url = current_url
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Status check error: {str(e)}{Style.RESET_ALL}")
time.sleep(1)
continue
time.sleep(1)
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication timeout{Style.RESET_ALL}")
return False, None
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication button not found{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Authentication failed: {str(e)}{Style.RESET_ALL}")
return False, None
finally:
if self.browser:
self.browser.quit()
def _extract_auth_info(self):
"""Extract authentication information after successful OAuth"""
try:
# Get cookies with retry
max_retries = 3
for attempt in range(max_retries):
try:
cookies = self.browser.cookies()
if cookies:
break
time.sleep(1)
except:
if attempt == max_retries - 1:
raise
time.sleep(1)
# Debug cookie information
print(f"{Fore.CYAN}{EMOJI['INFO']} Found {len(cookies)} cookies{Style.RESET_ALL}")
email = None
token = None
for cookie in cookies:
name = cookie.get("name", "")
if name == "WorkosCursorSessionToken":
try:
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Token extraction error: {str(e)}{Style.RESET_ALL}")
elif name == "cursor_email":
email = cookie.get("value")
if email and token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Authentication successful - Email: {email}{Style.RESET_ALL}")
return True, {"email": email, "token": token}
else:
missing = []
if not email:
missing.append("email")
if not token:
missing.append("token")
print(f"{Fore.RED}{EMOJI['ERROR']} Missing authentication data: {', '.join(missing)}{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to extract auth info: {str(e)}{Style.RESET_ALL}")
return False, None
def main(auth_type, translator=None):
"""Main function to handle OAuth authentication
Args:
auth_type (str): Type of authentication ('google' or 'github')
translator: Translator instance for internationalization
"""
handler = OAuthHandler(translator)
if auth_type.lower() == 'google':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start')}{Style.RESET_ALL}")
success, auth_info = handler.handle_google_auth()
elif auth_type.lower() == 'github':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start')}{Style.RESET_ALL}")
success, auth_info = handler.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid authentication type{Style.RESET_ALL}")
return False
if success and auth_info:
# Update Cursor authentication
auth_manager = CursorAuth(translator)
if auth_manager.update_auth(
email=auth_info["email"],
access_token=auth_info["token"],
refresh_token=auth_info["token"]
):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success')}{Style.RESET_ALL}")
# Close the browser after successful authentication
if handler.browser:
handler.browser.quit()
print(f"{Fore.CYAN}{EMOJI['INFO']} Browser closed{Style.RESET_ALL}")
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed')}{Style.RESET_ALL}")
return False

View File

@@ -32,7 +32,7 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
""" Get Cursor related paths"""
system = platform.system()
# 讀取配置文件
# Read config file
config = configparser.ConfigParser()
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
@@ -40,9 +40,9 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
if not os.path.exists(config_file):
raise OSError(translator.get('reset.config_not_found') if translator else "找不到配置文件")
config.read(config_file, encoding='utf-8') # 指定編碼
config.read(config_file, encoding='utf-8') # Specify encoding
# 根據系統獲取路徑
# Get path based on system
if system == "Darwin":
section = 'MacPaths'
elif system == "Windows":
@@ -63,7 +63,7 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
pkg_path = os.path.join(base_path, "package.json")
main_path = os.path.join(base_path, "out/main.js")
# 檢查文件是否存在
# Check if files exist
if not os.path.exists(pkg_path):
raise OSError(translator.get('reset.package_not_found', path=pkg_path) if translator else f"找不到 package.json: {pkg_path}")
if not os.path.exists(main_path):
@@ -187,7 +187,7 @@ def check_cursor_version(translator) -> bool:
with open(pkg_path, "r", encoding="utf-8") as f:
data = json.load(f)
except UnicodeDecodeError:
# 如果 UTF-8 讀取失敗,嘗試其他編碼
# If UTF-8 reading fails, try other encodings
with open(pkg_path, "r", encoding="latin-1") as f:
data = json.load(f)
@@ -206,15 +206,15 @@ def check_cursor_version(translator) -> bool:
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.found_version', version=version)}{Style.RESET_ALL}")
# 檢查版本格式
# Check version format
if not re.match(r"^\d+\.\d+\.\d+$", version):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
return False
# 比較版本
# Compare versions
try:
current = tuple(map(int, version.split(".")))
min_ver = (0, 45, 0) # 直接使用元組而不是字符串
min_ver = (0, 45, 0) # Use tuple directly instead of string
if current >= min_ver:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.version_check_passed', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
@@ -445,7 +445,7 @@ class MachineIDResetter:
elif sys.platform == "linux": # Linux
if not config.has_section('LinuxPaths'):
config.add_section('LinuxPaths')
# 获取实际用户的主目录
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")

15
scripts/install.sh Normal file → Executable file
View File

@@ -39,14 +39,17 @@ get_downloads_dir() {
# Get latest version
get_latest_version() {
echo -e "${CYAN} Checking latest version...${NC}"
local latest_release
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest)
if [ $? -ne 0 ]; then
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest) || {
echo -e "${RED}❌ Cannot get latest version information${NC}"
exit 1
fi
}
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
if [ -z "$VERSION" ]; then
echo -e "${RED}❌ Failed to parse version from GitHub API response:\n${latest_release}"
exit 1
fi
echo -e "${GREEN}✅ Found latest version: ${VERSION}${NC}"
}
@@ -170,9 +173,7 @@ install_cursor_free_vip() {
fi
echo -e "${CYAN} Setting executable permissions...${NC}"
chmod +x "${binary_path}"
if [ $? -eq 0 ]; then
if chmod +x "${binary_path}"; then
echo -e "${GREEN}✅ Installation completed!${NC}"
echo -e "${CYAN} Program downloaded to: ${binary_path}${NC}"
echo -e "${CYAN} Starting program...${NC}"

View File

@@ -1,6 +1,7 @@
import os
import sys
import platform
import random
def get_user_documents_path():
"""Get user documents path"""
@@ -28,5 +29,41 @@ def get_linux_cursor_path():
os.path.expanduser("~/.local/share/cursor/resources/app")
]
# 返回第一个存在的路径,如果都不存在则返回第一个路径
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0])
# return the first path that exists
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0])
def get_random_wait_time(config, timing_key):
"""Get random wait time based on configuration timing settings
Args:
config (dict): Configuration dictionary containing timing settings
timing_key (str): Key to look up in the timing settings
Returns:
float: Random wait time in seconds
"""
try:
# Get timing value from config
timing = config.get('Timing', {}).get(timing_key)
if not timing:
# Default to 0.5-1.5 seconds if timing not found
return random.uniform(0.5, 1.5)
# Check if timing is a range (e.g., "0.5-1.5" or "0.5,1.5")
if isinstance(timing, str):
if '-' in timing:
min_time, max_time = map(float, timing.split('-'))
elif ',' in timing:
min_time, max_time = map(float, timing.split(','))
else:
# Single value, use it as both min and max
min_time = max_time = float(timing)
else:
# If timing is a number, use it as both min and max
min_time = max_time = float(timing)
return random.uniform(min_time, max_time)
except (ValueError, TypeError, AttributeError):
# Return default value if any error occurs
return random.uniform(0.5, 1.5)