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.09
VERSION=1.7.06 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,5 +1,28 @@
# Change Log # 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 ## v1.7.06
1. Add: Update Confirm | 增加更新確認 1. Add: Update Confirm | 增加更新確認
2. Add: Update Skipped | 增加更新跳過 2. Add: Update Skipped | 增加更新跳過

View File

@@ -1,4 +1,5 @@
# ➤ Cursor Free VIP # ➤ Cursor Free VIP
<div align="center"> <div align="center">
<p align="center"> <p align="center">
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/> <img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
@@ -12,9 +13,10 @@
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
</p> </p>
<h4>Support Latest 0.46.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 的配置。
@@ -22,19 +24,22 @@ This is a tool to automatically register , support Windows and macOS systems, co
<img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br> <img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
</p> </p>
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/) ##### If you 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> </div>
## 🔄 Change Log | 更新日志 ## 🔄 Change Log | 更新日志
[Watch Change Log | 查看更新日志](CHANGELOG.md) [Watch Change Log | 查看更新日志](CHANGELOG.md)
## ✨ Features | 功能特點 ## ✨ Features | 功能特點
* 🌟 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> * Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br> * Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
@@ -43,11 +48,12 @@ This is a tool to automatically register , support Windows and macOS systems, co
* Reset Cursor's configuration<br>重置 Cursor 的配置<br> * Reset Cursor's configuration<br>重置 Cursor 的配置<br>
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
## 💻 System Support | 系統支持 ## 💻 System Support | 系統支持
| Windows | x64 | ✅ | macOS | Intel | ✅ | | Windows | x64 | ✅ | macOS | Intel | ✅ |
|:---:|:---:|:---:|:---:|:---:|:---:| |:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ | | Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
| Linux | x64 | ✅ | Linux | x86 | ✅ | | Linux | x64 | ✅ | Linux | x86 | ✅ |
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ | | Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
@@ -58,28 +64,34 @@ This is a tool to automatically register , support Windows and macOS systems, co
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary> <summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
**Linux/macOS** **Linux/macOS**
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
``` ```
**Windows** **Windows**
```powershell ```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
``` ```
</details> </details>
<details> <details>
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary> <summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
**Linux/macOS** **Linux/macOS**
```bash ```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
``` ```
**Windows** **Windows**
```powershell ```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
``` ```
</details> </details>
2. If you want to stop the script, please press Ctrl+C<br>要停止腳本,請按 Ctrl+C 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 | 最大超時時間
max_timeout = 160 max_timeout = 160
``` ```
</details> </details>
* Use administrator to run the script <br>請使用管理員身份運行腳本 * Use administrator to run the script <br>請使用管理員身份運行腳本
@@ -150,16 +163,12 @@ max_timeout = 160
* Please comply with the relevant software usage terms when using this tool <br>使用本工具時請遵守相關軟件使用條款 * Please comply with the relevant software usage terms when using this tool <br>使用本工具時請遵守相關軟件使用條款
## 🚨 Common Issues | 常見問題 ## 🚨 Common Issues | 常見問題
| 如果遇到權限問題,請確保: | 此腳本以管理員身份運行 | | 如果遇到權限問題,請確保: | 此腳本以管理員身份運行 |
|:---:|:---:| |:--------------------------------------------------:|:------------------------------------------------:|
| If you encounter permission issues, please ensure: | This script is run with administrator privileges | | If you encounter permission issues, please ensure: | This script is run with administrator privileges |
## 🤩 Contribution | 貢獻 ## 🤩 Contribution | 貢獻
歡迎提交 Issue 和 Pull Request 歡迎提交 Issue 和 Pull Request
@@ -170,12 +179,12 @@ max_timeout = 160
</a> </a>
<br /><br /> <br /><br />
## 📩 Disclaimer | 免責聲明 ## 📩 Disclaimer | 免責聲明
本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <br> 本工具僅供學習和研究使用,使用本工具所產生的任何後果由使用者自行承擔。 <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 | 請我喝杯咖啡 ## 💰 Buy Me a Coffee | 請我喝杯咖啡
@@ -204,7 +213,3 @@ This tool is only for learning and research purposes, and any consequences arisi
本項目採用 [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. Please refer to the [LICENSE](LICENSE.md) file for details.

View File

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

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ class CursorRegistration:
self.signup_tab = None self.signup_tab = None
self.email_tab = None self.email_tab = None
# 账号信息 # Account information
self.password = self._generate_password() self.password = self._generate_password()
# Generate first name and last name separately # Generate first name and last name separately
first_name = random.choice([ first_name = random.choice([
@@ -196,17 +196,17 @@ class CursorRegistration:
def _save_account_info(self, token, total_usage): def _save_account_info(self, token, total_usage):
"""Save Account Information to File""" """Save Account Information to File"""
try: try:
# 先更新认证信息 # Update authentication information first
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token): 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}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
else: else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}") 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}") print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
resetter = MachineIDResetter(self.translator) # 创建实例时传入translator resetter = MachineIDResetter(self.translator) # Pass translator when creating instance
if not resetter.reset_machine_ids(): # 直接调用reset_machine_ids方法 if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID") raise Exception("Failed to reset machine ID")
# Save account information to file # 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.translator = translator
self.system = platform.system() self.system = platform.system()
# 从配置文件获取路径 # Get path from configuration file
config = get_config(translator) config = get_config(translator)
if config: if config:
if self.system == "Windows": if self.system == "Windows":
@@ -36,7 +36,7 @@ class AutoUpdateDisabler:
elif self.system == "Linux": elif self.system == "Linux":
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater")) self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
else: else:
# 如果配置加载失败,使用默认路径 # If configuration loading fails, use default paths
self.updater_paths = { self.updater_paths = {
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"), "Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"),
"Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"), "Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"),

View File

@@ -4,6 +4,8 @@
"exit": "Exit Program", "exit": "Exit Program",
"reset": "Reset Machine ID", "reset": "Reset Machine ID",
"register": "Register New Cursor Account", "register": "Register New Cursor Account",
"register_google": "Register with Google Account",
"register_github": "Register with GitHub Account",
"register_manual": "Register Cursor with Custom Email", "register_manual": "Register Cursor with Custom Email",
"quit": "Close Cursor Application", "quit": "Close Cursor Application",
"select_language": "Change Language", "select_language": "Change Language",
@@ -12,12 +14,14 @@
"program_terminated": "Program was terminated by user", "program_terminated": "Program was terminated by user",
"error_occurred": "An error occurred: {error}. Please try again", "error_occurred": "An error occurred: {error}. Please try again",
"press_enter": "Press Enter to Exit", "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": { "languages": {
"en": "English", "en": "English",
"zh_cn": "简体中文", "zh_cn": "简体中文",
"zh_tw": "繁體中文" "zh_tw": "繁體中文",
"vi": "Vietnamese"
}, },
"quit_cursor": { "quit_cursor": {
"start": "Start Quitting Cursor", "start": "Start Quitting Cursor",
@@ -275,6 +279,7 @@
"continue_anyway": "Continuing with current version...", "continue_anyway": "Continuing with current version...",
"update_confirm": "Do you want to update to the latest version? (Y/n)", "update_confirm": "Do you want to update to the latest version? (Y/n)",
"update_skipped": "Skipping update.", "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": "退出程序", "exit": "退出程序",
"reset": "重置机器标识", "reset": "重置机器标识",
"register": "注册新 Cursor 账号", "register": "注册新 Cursor 账号",
"register_google": "使用 Google 账号注册",
"register_github": "使用 GitHub 账号注册",
"register_manual": "使用自定义邮箱注册", "register_manual": "使用自定义邮箱注册",
"quit": "关闭 Cursor 应用", "quit": "关闭 Cursor 应用",
"select_language": "更改语言", "select_language": "更改语言",
@@ -12,12 +14,14 @@
"program_terminated": "程序已被用户终止", "program_terminated": "程序已被用户终止",
"error_occurred": "发生错误:{error},请重试", "error_occurred": "发生错误:{error},请重试",
"press_enter": "按回车键退出", "press_enter": "按回车键退出",
"disable_auto_update": "禁用 Cursor 自动更新" "disable_auto_update": "禁用 Cursor 自动更新",
"lifetime_access_enabled": "永久订阅"
}, },
"languages": { "languages": {
"en": "English", "en": "English",
"zh_cn": "简体中文", "zh_cn": "简体中文",
"zh_tw": "繁體中文" "zh_tw": "繁體中文",
"vi": "Vietnamese"
}, },
"quit_cursor": { "quit_cursor": {
"start": "开始退出 Cursor", "start": "开始退出 Cursor",
@@ -271,6 +275,7 @@
"continue_anyway": "继续使用当前版本...", "continue_anyway": "继续使用当前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)", "update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳过更新。", "update_skipped": "跳过更新。",
"invalid_choice": "选择无效。请输入 'Y' 或 'n'." "invalid_choice": "选择无效。请输入 'Y' 或 'n'.",
"development_version": "开发版本 {current} > {latest}"
} }
} }

View File

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

125
main.py
View File

@@ -11,16 +11,16 @@ import requests
import subprocess import subprocess
from config import get_config from config import get_config
# 只在 Windows 系统上导入 windll # Only import windll on Windows systems
if platform.system() == 'Windows': if platform.system() == 'Windows':
import ctypes import ctypes
# 只在 Windows 上导入 windll # 只在 Windows 上导入 windll
from ctypes import windll from ctypes import windll
# 初始化colorama # Initialize colorama
init() init()
# 定义emoji和颜色常量 # Define emoji and color constants
EMOJI = { EMOJI = {
"FILE": "📄", "FILE": "📄",
"BACKUP": "💾", "BACKUP": "💾",
@@ -31,13 +31,47 @@ EMOJI = {
"MENU": "📋", "MENU": "📋",
"ARROW": "", "ARROW": "",
"LANG": "🌐", "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: class Translator:
def __init__(self): def __init__(self):
self.translations = {} 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.fallback_language = 'en' # Fallback language if translation is missing
self.load_translations() self.load_translations()
@@ -58,11 +92,11 @@ class Translator:
def _detect_windows_language(self): def _detect_windows_language(self):
"""Detect language on Windows systems""" """Detect language on Windows systems"""
try: try:
# 确保我们在 Windows # Ensure we are on Windows
if platform.system() != 'Windows': if platform.system() != 'Windows':
return 'en' return 'en'
# 获取键盘布局 # Get keyboard layout
user32 = ctypes.windll.user32 user32 = ctypes.windll.user32
hwnd = user32.GetForegroundWindow() hwnd = user32.GetForegroundWindow()
threadid = user32.GetWindowThreadProcessId(hwnd, 0) threadid = user32.GetWindowThreadProcessId(hwnd, 0)
@@ -73,6 +107,7 @@ class Translator:
0x0409: 'en', # English 0x0409: 'en', # English
0x0404: 'zh_tw', # Traditional Chinese 0x0404: 'zh_tw', # Traditional Chinese
0x0804: 'zh_cn', # Simplified Chinese 0x0804: 'zh_cn', # Simplified Chinese
0x0422: 'vi', # Vietnamese
} }
return language_map.get(layout_id, 'en') return language_map.get(layout_id, 'en')
@@ -96,6 +131,9 @@ class Translator:
return 'zh_cn' return 'zh_cn'
elif system_locale.startswith('en'): elif system_locale.startswith('en'):
return 'en' return 'en'
elif system_locale.startswith('vi'):
return 'vi'
# Try to get language from LANG environment variable as fallback # Try to get language from LANG environment variable as fallback
env_lang = os.getenv('LANG', '').lower() env_lang = os.getenv('LANG', '').lower()
@@ -103,6 +141,9 @@ class Translator:
return 'zh_tw' return 'zh_tw'
elif 'cn' in env_lang: elif 'cn' in env_lang:
return 'zh_cn' return 'zh_cn'
elif 'vi' in env_lang:
return 'vi'
return 'en' return 'en'
except: except:
@@ -168,20 +209,24 @@ class Translator:
"""Get list of available languages""" """Get list of available languages"""
return list(self.translations.keys()) return list(self.translations.keys())
# 创建翻译器实例 # Create translator instance
translator = Translator() translator = Translator()
def print_menu(): def print_menu():
"""打印菜单选项""" """Print menu options"""
print(f"\n{Fore.CYAN}{EMOJI['MENU']} {translator.get('menu.title')}:{Style.RESET_ALL}") 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.YELLOW}{'' * 40}{Style.RESET_ALL}")
print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}") print(f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}")
print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}") print(f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}")
print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}") print(f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')}")
print(f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}") print(f"{Fore.GREEN}3{Style.RESET_ALL}. 🌟 {translator.get('menu.register_google')}")
print(f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}") print(f"{Fore.YELLOW} ┗━━ 🔥 {translator.get('menu.lifetime_access_enabled')} 🔥{Style.RESET_ALL}")
print(f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}") print(f"{Fore.GREEN}4{Style.RESET_ALL}. {translator.get('menu.register_github')}")
print(f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}") 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}") print(f"{Fore.YELLOW}{'' * 40}{Style.RESET_ALL}")
def select_language(): def select_language():
@@ -236,10 +281,30 @@ def check_latest_version():
if not latest_version: if not latest_version:
raise Exception("Invalid version format received") 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}") 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: while True:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('updater.update_confirm', choices='Y/n')}: {Style.RESET_ALL}").lower() 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']: if choice in ['', 'y', 'yes']:
@@ -282,6 +347,10 @@ def check_latest_version():
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.update_failed', error=str(update_error))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.update_failed', error=str(update_error))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.manual_update_required')}{Style.RESET_ALL}") print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.manual_update_required')}{Style.RESET_ALL}")
return return
else:
# 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: else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
@@ -296,9 +365,17 @@ def check_latest_version():
return return
def main(): 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() print_logo()
# 初始化配置 # Initialize configuration
config = get_config(translator) config = get_config(translator)
if not config: if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
@@ -309,7 +386,7 @@ def main():
while True: while True:
try: 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": if choice == "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}") print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
@@ -324,18 +401,26 @@ def main():
cursor_register.main(translator) cursor_register.main(translator)
print_menu() print_menu()
elif choice == "3": 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 import cursor_register_manual
cursor_register_manual.main(translator) cursor_register_manual.main(translator)
print_menu() print_menu()
elif choice == "4": elif choice == "6":
import quit_cursor import quit_cursor
quit_cursor.quit_cursor(translator) quit_cursor.quit_cursor(translator)
print_menu() print_menu()
elif choice == "5": elif choice == "7":
if select_language(): if select_language():
print_menu() print_menu()
continue continue
elif choice == "6": elif choice == "8":
import disable_auto_update import disable_auto_update
disable_auto_update.run(translator) disable_auto_update.run(translator)
print_menu() print_menu()

View File

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

View File

@@ -263,7 +263,7 @@ class NewTempEmail:
return False return False
def check_for_cursor_email(self): def check_for_cursor_email(self):
"""检查是否有 Cursor 的验证邮件""" """Check if there is a Cursor verification email"""
try: try:
# Use API to get email list # Use API to get email list
headers = {"Authorization": f"Bearer {self.token}"} 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""" """ Get Cursor related paths"""
system = platform.system() system = platform.system()
# 讀取配置文件 # Read config file
config = configparser.ConfigParser() config = configparser.ConfigParser()
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip") config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini") 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): if not os.path.exists(config_file):
raise OSError(translator.get('reset.config_not_found') if translator else "找不到配置文件") 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": if system == "Darwin":
section = 'MacPaths' section = 'MacPaths'
elif system == "Windows": 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") pkg_path = os.path.join(base_path, "package.json")
main_path = os.path.join(base_path, "out/main.js") main_path = os.path.join(base_path, "out/main.js")
# 檢查文件是否存在 # Check if files exist
if not os.path.exists(pkg_path): 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}") 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): 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: with open(pkg_path, "r", encoding="utf-8") as f:
data = json.load(f) data = json.load(f)
except UnicodeDecodeError: except UnicodeDecodeError:
# 如果 UTF-8 讀取失敗,嘗試其他編碼 # If UTF-8 reading fails, try other encodings
with open(pkg_path, "r", encoding="latin-1") as f: with open(pkg_path, "r", encoding="latin-1") as f:
data = json.load(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}") 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): 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}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
return False return False
# 比較版本 # Compare versions
try: try:
current = tuple(map(int, version.split("."))) 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: 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}") 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 elif sys.platform == "linux": # Linux
if not config.has_section('LinuxPaths'): if not config.has_section('LinuxPaths'):
config.add_section('LinuxPaths') config.add_section('LinuxPaths')
# 获取实际用户的主目录 # Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER') sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~") 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
get_latest_version() { get_latest_version() {
echo -e "${CYAN} Checking latest version...${NC}" 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) || {
latest_release=$(curl -s https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest)
if [ $? -ne 0 ]; then
echo -e "${RED}❌ Cannot get latest version information${NC}" echo -e "${RED}❌ Cannot get latest version information${NC}"
exit 1 exit 1
}
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 fi
VERSION=$(echo "$latest_release" | grep -o '"tag_name": ".*"' | cut -d'"' -f4 | tr -d 'v')
echo -e "${GREEN}✅ Found latest version: ${VERSION}${NC}" echo -e "${GREEN}✅ Found latest version: ${VERSION}${NC}"
} }
@@ -170,9 +173,7 @@ install_cursor_free_vip() {
fi fi
echo -e "${CYAN} Setting executable permissions...${NC}" echo -e "${CYAN} Setting executable permissions...${NC}"
chmod +x "${binary_path}" if chmod +x "${binary_path}"; then
if [ $? -eq 0 ]; then
echo -e "${GREEN}✅ Installation completed!${NC}" echo -e "${GREEN}✅ Installation completed!${NC}"
echo -e "${CYAN} Program downloaded to: ${binary_path}${NC}" echo -e "${CYAN} Program downloaded to: ${binary_path}${NC}"
echo -e "${CYAN} Starting program...${NC}" echo -e "${CYAN} Starting program...${NC}"

View File

@@ -1,6 +1,7 @@
import os import os
import sys import sys
import platform import platform
import random
def get_user_documents_path(): def get_user_documents_path():
"""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") os.path.expanduser("~/.local/share/cursor/resources/app")
] ]
# 返回第一个存在的路径,如果都不存在则返回第一个路径 # return the first path that exists
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0]) 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)