Compare commits

...

66 Commits

Author SHA1 Message Date
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
yeongpin
ff358588bb feat: Refactor Project Structure and Add Configuration Management
- Add `config.py` for centralized configuration management
- Add `utils.py` for cross-platform utility functions
- Remove `browser.py` and `control.py` to simplify project structure
- Update version to 1.7.06
- Modify build specification to reflect new file structure
- Update localization files with new update confirmation messages
- Enhance configuration loading and path detection across different platforms
2025-03-11 11:49:17 +08:00
yeongpin
6ca80ccb10 Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-03-11 10:12:57 +08:00
yeongpin
2fca5218fb remove md 2025-03-11 10:12:54 +08:00
Pin Studios
71ecf5a201 Update issue templates 2025-03-11 10:12:41 +08:00
Pin Studios
4570b174ab Update issue templates 2025-03-10 18:36:36 +08:00
yeongpin
f708ce443b fix: Correct Syntax in Linux Installation Script
- Fix syntax error in install script's conditional statements
- Remove unnecessary braces and correct shell script syntax
- Ensure proper handling of Linux architecture detection
2025-03-10 17:54:09 +08:00
yeongpin
4f6f3fe814 fix: Improve Cursor Version Check and Configuration Handling
- Enhance version checking in `reset_machine_manual.py` with more robust error handling
- Add detailed error messages and logging for version detection
- Update configuration paths in `new_signup.py` to include Cursor application path
- Add configuration initialization in `main.py`
- Update localization files with new error and version-related messages
2025-03-10 17:41:07 +08:00
yeongpin
54ecf2d752 feat: Add Linux ARM64 Support and Update Build Workflow
- Extend GitHub Actions workflow to build Linux x64 and ARM64 executables
- Update install script to detect Linux architecture (x64 or ARM64)
- Modify release process to include both Linux architecture artifacts
- Rename Linux build job to clarify x64 architecture
2025-03-10 16:28:26 +08:00
yeongpin
1f1231d1a9 remove br in readme 2025-03-10 14:43:35 +08:00
yeongpin
d10999a517 Update Readme Doc 2025-03-10 14:43:09 +08:00
yeongpin
fb4be6334a docs: Add Detailed Comments and Localization for Configuration Settings
- Add bilingual comments (English and Chinese) for OSPaths section
- Provide clear descriptions for timing-related configuration parameters
- Enhance readability of README.md configuration settings
- Improve documentation for storage, SQLite, and machine ID paths
2025-03-10 13:42:18 +08:00
Pin Studios
786eba5371 Update README.md 2025-03-10 13:41:05 +08:00
yeongpin
41ddbf519e hotfix: Optimize Verification Code Handling and Timing Configuration
- Refactor verification code retrieval with dynamic wait time generation
- Use `get_random_wait_time()` for more flexible retry intervals
- Update version to 1.7.04 across project files
- Minor improvements to timing and retry logic in signup process
2025-03-10 13:27:41 +08:00
yeongpin
ffd48201fd hotfix: Improve Signup Flow and Timing Configuration
- Add comprehensive timing configuration for signup process
- Refactor random wait time generation with more flexible config options
- Update form filling and verification code handling with configurable wait times
- Enhance localization support for new timing-related messages
- Update version to 1.7.03 across project files
2025-03-10 12:24:43 +08:00
yeongpin
cd4f36725c refactor: Optimize Signup Flow and Error Handling
- Implement direct URL-based signup with pre-filled parameters
- Enhance password setting process with improved error detection
- Add more robust retry mechanism for password input
- Improve error handling for email availability and submission
- Refactor signup and password setting methods for better reliability
2025-03-10 11:44:37 +08:00
yeongpin
dccb524bd7 Update Readme add some doc 2025-03-10 11:21:42 +08:00
yeongpin
90e9a5b287 feat: Enhance Configuration Management for Cursor Paths
- Add OS-specific path configurations for Cursor storage, SQLite, and machine ID files
- Integrate configuration file management in `new_signup.py` and `reset_machine_manual.py`
- Remove Workbench Cursor path and main.js modification steps
- Improve cross-platform path detection and configuration handling
- Update version to 1.7.02
2025-03-10 11:18:21 +08:00
Pin Studios
66a67fce8b Update README.md 2025-03-07 11:20:04 +08:00
Pin Studios
0981f00b9c Update build.yml 2025-03-07 11:19:49 +08:00
yeongpin
02851c9a09 feat: Implement Configuration Management and Enhance Browser Setup
- Add `setup_config` function to manage configuration file across platforms
- Extract configuration-related code from `setup_driver` into a separate function
- Implement dynamic Chrome path detection for Windows, macOS, and Linux
- Add configurable Turnstile verification settings
- Update README.md with configuration file details
- Enhance localization support for configuration-related messages
- Improve code maintainability and platform compatibility
2025-03-07 11:18:43 +08:00
yeongpin
6312d66813 chore: Bump version to 1.6.03
- Update version in .env file
- Update CHANGELOG.md with version 1.6.03 details
- Minor hotfix and maintenance release
2025-03-06 18:37:04 +08:00
yeongpin
1b1a21f3d7 feat: Add Machine ID File Update Functionality
- Implement `update_machine_id_file` method in `MachineIDResetter` class
- Add new `get_cursor_machine_id_path` function to detect machineId file path across platforms
- Enhance machine ID reset process with file-level machine ID update
- Implement backup mechanism for existing machineId file
- Add cross-platform support for Linux, macOS, and Windows
- Improve error handling and logging with colorful console output
2025-03-06 18:36:00 +08:00
yeongpin
c94bd605e5 fix: Update GitHub Link in Workbench JS Modification
- Correct GitHub repository link to point to specific project repository
- Modify onClick handler to open the correct GitHub page for the project
2025-03-06 18:14:34 +08:00
yeongpin
9f6eee77e0 feat: Add Workbench JS Modification and Cursor Path Detection
- Update `MachineIDResetter` to include workbench.desktop.main.js modification step
- Enhance platform support for Linux, macOS, and Windows
- Update CHANGELOG.md to version 1.6.02
2025-03-06 18:11:40 +08:00
Pin Studios
2bdcc2f633 chore: Bump version to 1.6.01 2025-03-06 12:19:26 +08:00
Pin Studios
4aabe2e403 chore: Bump version to 1.5.04 and improve system compatibility
- Update version in .env file to 1.5.04
- Add Mac-specific run_venv script to .gitignore
- Enhance Cursor Auth platform detection with more precise sys.platform checks
- Add maximum retry mechanism for email creation
- Improve error handling and platform support in cursor_auth.py
2025-03-06 12:18:55 +08:00
yeongpin
005aa2cd95 feat: Improve Version Update Mechanism with Robust Error Handling
- Enhance GitHub API version check with proper headers and timeout
- Add comprehensive error handling for version retrieval
- Implement more reliable update script download and execution
- Improve cross-platform update process with better error reporting
- Add fallback mechanisms for network and update failures
2025-03-06 11:18:21 +08:00
yeongpin
14f6dfc29d fix: Improve Browser Startup and Error Handling
- Add `--no-sandbox` flag to resolve browser startup issues
- Enhance error handling in temp email creation
- Update localization files with new email-related messages
- Improve translation support for email creation process
2025-03-06 11:01:34 +08:00
Pin Studios
2e9bd269ad Update README.md 2025-03-03 15:24:36 +08:00
Pin Studios
f9b7e23253 Update README.md 2025-03-03 15:19:44 +08:00
yeongpin
0d979f7543 chore: Bump default version to 1.5.03 in build workflow 2025-03-03 11:59:15 +08:00
yeongpin
cce3025f7f feat: Enhance Name Generation and Improve Account Registration Process
- Implement realistic name generation with predefined name lists
- Modify first name letter for uniqueness
- Add more descriptive console output with emojis and translations
- Update localization files with new registration-related keys
- Optimize random name generation in registration modules
2025-03-03 11:58:28 +08:00
Pin Studios
fe3e27561b Update .env 2025-03-03 11:17:35 +08:00
yeongpin
bf2bea71eb Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-03-03 11:16:45 +08:00
yeongpin
77a61647dd docs: Update CHANGELOG with version 1.5.01 release notes 2025-03-03 11:10:10 +08:00
yeongpin
813dd4431e feat: Add version update checker with GitHub release support 2025-03-03 11:09:11 +08:00
Pin Studios
d7116b8cf3 Update .env 2025-03-02 13:55:25 +08:00
Pin Studios
f5a7acc4e3 Merge pull request #122 from bingoohuang/main
print account info as early as possible
2025-03-02 13:54:58 +08:00
bingoohuang
479933844a print account info as early as possible 2025-02-28 21:20:12 +08:00
yeongpin
93046d7f03 Update README.md with improved image styling and new screenshot 2025-02-27 10:44:18 +08:00
Pin Studios
e7ca31b710 Update build.yml 2025-02-27 10:33:53 +08:00
Pin Studios
87b99b0d16 Update CHANGELOG.md 2025-02-27 10:29:10 +08:00
Pin Studios
e983b6f560 Update .env 2025-02-27 10:27:18 +08:00
Pin Studios
21ca7ab24f Merge pull request #111 from ahmed98Osama/feature/return-to-menu
Return to main menu after each operation
2025-02-27 10:27:00 +08:00
Ahmed Osama
e7468644a4 feat: return to main menu after each operation instead of exiting 2025-02-26 19:07:14 +02:00
Pin Studios
2bca7d7d14 Update install.sh 2025-02-26 22:34:38 +08:00
Pin Studios
040c5f5836 Update install.ps1 2025-02-26 22:30:56 +08:00
Pin Studios
4a86bbeeb4 Update block_domain.txt 2025-02-26 22:01:53 +08:00
Pin Studios
68b7888a83 Merge pull request #101 from aliensb/main
add support for linux
2025-02-25 23:56:29 +08:00
yeongpin
78dee025a7 feat: Enhance Temporary Email Service with Domain Blocking and Improved Error Handling
- Add domain blocking mechanism to filter out problematic email domains
- Implement dynamic domain list retrieval from external source
- Improve error handling and logging in email creation process
- Add fallback service switching when domains are blocked
- Update localization files with new error and blocking-related messages
- Increment version to 1.4.06
2025-02-25 23:53:55 +08:00
Pin Studios
17c2b4b243 Create block_domain.txt 2025-02-25 23:30:53 +08:00
tom
2acb271f19 add support for linux 2025-02-25 16:52:32 +08:00
Pin Studios
b688b67c26 Update .gitignore 2025-02-25 12:23:45 +08:00
30 changed files with 2518 additions and 880 deletions

4
.env
View File

@@ -1,2 +1,2 @@
version=1.4.05 version=1.7.07
VERSION=1.4.05 VERSION=1.7.07

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

@@ -6,7 +6,7 @@ on:
version: version:
description: 'Version number (e.g. 1.0.9)' description: 'Version number (e.g. 1.0.9)'
required: true required: true
default: '1.4.01' default: '1.7.06'
permissions: permissions:
contents: write contents: write
@@ -98,7 +98,7 @@ jobs:
name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64 name: CursorFreeVIP_${{ env.VERSION }}_mac_arm64
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
build-linux: build-linux-x64:
needs: create-tag needs: create-tag
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
@@ -120,20 +120,55 @@ jobs:
pip install pyinstaller pip install pyinstaller
pip install -r requirements.txt pip install -r requirements.txt
- name: Build Linux executable - name: Build Linux x64 executable
env: env:
VERSION: ${{ env.VERSION }} VERSION: ${{ env.VERSION }}
run: | run: |
pyinstaller build.spec pyinstaller build.spec
mv "dist/CursorFreeVIP_${{ env.VERSION }}_linux" "dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64"
echo "Contents of dist directory:" echo "Contents of dist directory:"
ls -la dist/ ls -la dist/
- name: Upload Linux artifact - name: Upload Linux x64 artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: CursorFreeVIP_${{ env.VERSION }}_linux name: CursorFreeVIP_${{ env.VERSION }}_linux_x64
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_x64
build-linux-arm64:
needs: create-tag
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
with:
platforms: arm64
- name: Set version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
- name: Build in ARM64 Docker container
run: |
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.9-slim bash -c "
apt-get update && apt-get install -y build-essential
pip install --upgrade pip
pip install pyinstaller
pip install -r requirements.txt
python -m PyInstaller build.spec
mv /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux /app/dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
"
echo "Contents of dist directory:"
ls -la dist/
- name: Upload Linux ARM64 artifact
uses: actions/upload-artifact@v4
with:
name: CursorFreeVIP_${{ env.VERSION }}_linux_arm64
path: dist/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
build-macos-intel: build-macos-intel:
needs: create-tag needs: create-tag
@@ -171,9 +206,8 @@ jobs:
name: CursorFreeVIP_${{ env.VERSION }}_mac_intel name: CursorFreeVIP_${{ env.VERSION }}_mac_intel
path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel path: dist/CursorFreeVIP_${{ env.VERSION }}_mac_intel
create-release: create-release:
needs: [build-windows, build-macos-arm64, build-linux, build-macos-intel] needs: [build-windows, build-macos-arm64, build-linux-x64, build-linux-arm64, build-macos-intel]
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
@@ -201,7 +235,8 @@ jobs:
files: | files: |
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64 artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux/CursorFreeVIP_${{ env.VERSION }}_linux artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_x64/CursorFreeVIP_${{ env.VERSION }}_linux_x64
artifacts/CursorFreeVIP_${{ env.VERSION }}_linux_arm64/CursorFreeVIP_${{ env.VERSION }}_linux_arm64
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_intel/CursorFreeVIP_${{ env.VERSION }}_mac_intel
draft: false draft: false
prerelease: false prerelease: false

53
.gitignore vendored
View File

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

View File

@@ -1,5 +1,99 @@
# Change Log # Change Log
## v1.7.07
1. Add: Vietnamese Language | 增加越南語言
2. Add: Admin Privilege Management for Windows Executable | 增加Windows可執行文件管理員權限
3. Implement admin privilege detection for Windows platform | 實現Windows平台管理員權限檢測
4. Add functions to check and request admin rights when running as a frozen executable | 增加檢查和請求管理員權限的功能
5. Enhance startup process with admin privilege verification | 增強啟動過程中的管理員權限驗證
6. Add new admin-related emoji to the EMOJI dictionary | 增加新的管理員相關表情符號到EMOJI字典
7. Provide fallback mechanism for non-Windows platforms (macos and linux ) | 提供非Windows平台macos和linux的回退機制
These changes make the application more user-friendly by only requesting admin privileges when necessary (when running as an executable). | 這些改進使應用程序更易於使用,只在必要時(當作為可執行文件運行時)請求管理員權限。
## v1.7.06
1. Add: Update Confirm | 增加更新確認
2. Add: Update Skipped | 增加更新跳過
3. Add: Invalid Choice | 增加無效選擇
4. Fix: Cursor Path | 修復Cursor路徑
5. Fix: Path Encoding | 修復路徑編碼
6. Fix: Getting Verification Code | 修復獲取驗證碼
7. Fix: Setting Password | 修復設置密碼
8. Fix: Disable Auto Update | 修復禁用自動更新
9. Add Config.py | 增加Config.py
10. Add utils.py | 增加utils.py
11. Rebuild some logic | 重新構建一些邏輯
## v1.7.05
1. Fix: Cursor Version Check | 修復Cursor版本檢查
2. Fix: Small Problem | 修復一些小問題
## v1.7.04
1. Hotfix: Small Problem | 修復一些小問題
## v1.7.03
1. Hotfix: Small Problem | 修復一些小問題
## v1.7.02
1. Fix: Cursor Path | 修復Cursor路徑
2. Add: Config File | 增加配置文件
3. Remove: Workbench Cursor Path | 移除Workbench Cursor路徑
4. Remove: Cursor Main JS | 移除Cursor main.js
## v1.7.01
- Refactoring: Extract configuration-related code from the `setup_driver` function to an independent `setup_config` function
- Optimization: Improve code maintainability and make configuration management and browser settings more clear
- Improvement: The creation and update logic of the configuration file is clearer and more independent
## v1.6.03
1. Hotfix: Small Problem | 修復一些問題
## v1.6.02
1. Hotfix: Small Problem | 修復一些問題
2. Add: Test some Bypass Code | 測試一些繞過代碼
## v1.6.01
1. Fix: Cursor Auth | 修復Cursor Auth
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
3. Fix: Cursor Auth Error | 修復Cursor Auth錯誤
4. Fix: Update Curl Faild | 修復更新Curl失敗
## v1.5.03
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
2. Small Fix: Error Handling | 小修錯誤處理
3. Small Fix: Translation | 小修翻譯
4. Small Fix: Performance | 小修性能
## v1.5.02
1. Add: Generate Random Name Alias | 增加生成隨機真實姓名
2. Add: Realistic Name Input | 增加真實姓名輸入
3. Optimize: Error Handling | 優化錯誤處理
4. Optimize: Translation | 優化翻譯
5. Optimize: Performance | 優化性能
## v1.5.01
1. Add: Check Latest Version | 增加檢查最新版本
2. Add: Update Command | 增加更新命令
## v1.4.08
1. Add: Print Some Account Info | 增加打印一些賬號信息
## v1.4.07
1. Add Removed break statements after each operation | 修改結束event後的break暫停應用
2. Added print_menu() calls to show the menu again | 添加print_menu調用以再次顯示菜單
3. Updated error handling to show menu instead of exiting | 更新錯誤處理以顯示菜單而不是退出
## v1.4.06
1. Add: Blocked Domains Loaded | 增加被屏蔽的域名加載
2. Fix: Cleanup Error | 修復清理進程時出錯
3. Fix: Blocked Domains Loaded Error | 修復被屏蔽的域名加載錯誤
4. Fix: Available Domains Loaded Error | 修復可用域名加載錯誤
5. Fix: Domains Filtered Error | 修復過濾後剩餘域名錯誤
6. Fix: Domains Excluded Error | 修復排除域名錯誤
## v1.4.05 ## v1.4.05
1. Fix: macOS Language Detection | 修復macOS語言檢測 1. Fix: macOS Language Detection | 修復macOS語言檢測

View File

@@ -1,7 +1,7 @@
# ➤ Cursor Free VIP # ➤ Cursor Free VIP
<div align="center"> <div align="center">
<p align="center"> <p align="center">
<img src="./images/logo.png" alt="Cursor Pro Logo" width="200"/> <img src="./images/logo.png" alt="Cursor Pro Logo" width="200" style="border-radius: 6px;"/>
</p> </p>
<p align="center"> <p align="center">
@@ -12,14 +12,14 @@
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
</p> </p>
<h4>Support Latest 0.46.3 Version | 支持最新0.46.3本</h4> <h4>Support Latest 0.47.x Version | 支持最新0.47.x版本</h4>
This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration. This is a tool to automatically register , support Windows and macOS systems, complete Auth verification, and reset Cursor's configuration.
這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。 這是一個自動化工具,自動註冊 ,支持 Windows 和 macOS 系統完成Auth驗證重置Cursor的配置。
<p align="center"> <p align="center">
<img src="./images/pronew_2025-02-13_15-01-32.png" alt="new" width="400"/><br> <img src="./images/new_2025-02-27_10-42-44.png" alt="new" width="400" style="border-radius: 6px;"/><br>
</p> </p>
##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/) ##### If you dont have google chrome , you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
@@ -86,6 +86,62 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
## ❗ Note | 注意事項 ## ❗ Note | 注意事項
📝 Config | 文件配置
`Win / Macos / Linux Path | 路徑 [Documents/.cursor-free-vip/config.ini]`
<details>
<summary><b>⭐ Config | 文件配置</b></summary>
```
[Chrome]
# Default Google Chrome Path | 默認Google Chrome 遊覽器路徑
chromepath = C:\Program Files\Google/Chrome/Application/chrome.exe
[Turnstile]
# Handle Tuenstile Wait Time | 等待人機驗證時間
handle_turnstile_time = 2
# Handle Tuenstile Wait Random Time (must merge 1-3 or 1,3) | 等待人機驗證隨機時間(必須是 1-3 或者 1,3 這樣的組合)
handle_turnstile_random_time = 1-3
[OSPaths]
# Storage Path | 存儲路徑
storage_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/storage.json
# SQLite Path | SQLite路徑
sqlite_path = /Users/username/Library/Application Support/Cursor/User/globalStorage/state.vscdb
# Machine ID Path | 機器ID路徑
machine_id_path = /Users/username/Library/Application Support/Cursor/machineId
[Timing]
# Min Random Time | 最小隨機時間
min_random_time = 0.1
# Max Random Time | 最大隨機時間
max_random_time = 0.8
# Page Load Wait | 頁面加載等待時間
page_load_wait = 0.1-0.8
# Input Wait | 輸入等待時間
input_wait = 0.3-0.8
# Submit Wait | 提交等待時間
submit_wait = 0.5-1.5
# Verification Code Input | 驗證碼輸入等待時間
verification_code_input = 0.1-0.3
# Verification Success Wait | 驗證成功等待時間
verification_success_wait = 2-3
# Verification Retry Wait | 驗證重試等待時間
verification_retry_wait = 2-3
# Email Check Initial Wait | 郵件檢查初始等待時間
email_check_initial_wait = 4-6
# Email Refresh Wait | 郵件刷新等待時間
email_refresh_wait = 2-4
# Settings Page Load Wait | 設置頁面加載等待時間
settings_page_load_wait = 1-2
# Failed Retry Time | 失敗重試時間
failed_retry_time = 0.5-1
# Retry Interval | 重試間隔
retry_interval = 8-12
# Max Timeout | 最大超時時間
max_timeout = 160
```
</details>
* Use administrator to run the script <br>請使用管理員身份運行腳本 * Use administrator to run the script <br>請使用管理員身份運行腳本
* Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br> * Confirm that Cursor is closed before running the script <br>請確保在運行腳本前已經關閉 Cursor<br>
@@ -98,9 +154,9 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
## 🚨 Common Issues | 常見問題 ## 🚨 Common Issues | 常見問題
|如果遇到權限問題,請確保:|If you encounter permission issues, please ensure:| |如果遇到權限問題,請確保:| 此腳本以管理員身份運行 |
|:---:|:---:| |:---:|:---:|
| 此腳本以管理員身份運行 | This script is run with administrator privileges | |If you encounter permission issues, please ensure: | This script is run with administrator privileges |
@@ -109,6 +165,11 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/rese
歡迎提交 Issue 和 Pull Request 歡迎提交 Issue 和 Pull Request
<a href="https://github.com/yeongpin/cursor-free-vip/graphs/contributors">
<img src="https://contrib.rocks/image?repo=yeongpin/cursor-free-vip" />
</a>
<br /><br />
## 📩 Disclaimer | 免責聲明 ## 📩 Disclaimer | 免責聲明

3
block_domain.txt Normal file
View File

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

View File

@@ -1,95 +0,0 @@
from DrissionPage import ChromiumOptions, ChromiumPage
import sys
import os
import logging
import random
class BrowserManager:
def __init__(self, noheader=False):
self.browser = None
self.noheader = noheader
def init_browser(self):
"""初始化浏览器"""
co = self._get_browser_options()
# 如果设置了 noheader添加相应的参数
if self.noheader:
co.set_argument('--headless=new')
self.browser = ChromiumPage(co)
return self.browser
def _get_browser_options(self):
"""获取浏览器配置"""
co = ChromiumOptions()
try:
extension_path = self._get_extension_path()
co.add_extension(extension_path)
co.set_argument("--allow-extensions-in-incognito")
extension_block_path = self.get_extension_block()
co.add_extension(extension_block_path)
co.set_argument("--allow-extensions-in-incognito")
except FileNotFoundError as e:
logging.warning(f"警告: {e}")
# 设置更真实的用户代理
co.set_user_agent(
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
)
# 基本设置
co.set_pref("credentials_enable_service", False)
# 随机端口
co.auto_port()
# 系统特定设置
if sys.platform == "darwin": # macOS
co.set_argument("--disable-gpu")
co.set_argument("--no-sandbox")
elif sys.platform == "win32": # Windows
co.set_argument("--disable-software-rasterizer")
# 设置窗口大小
window_width = random.randint(1024, 1920)
window_height = random.randint(768, 1080)
co.set_argument(f"--window-size={window_width},{window_height}")
return co
def _get_extension_path(self):
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "turnstilePatch")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "turnstilePatch")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def get_extension_block(self):
"""获取插件路径"""
root_dir = os.getcwd()
extension_path = os.path.join(root_dir, "PBlock")
if hasattr(sys, "_MEIPASS"):
extension_path = os.path.join(sys._MEIPASS, "PBlock")
if not os.path.exists(extension_path):
raise FileNotFoundError(f"插件不存在: {extension_path}")
return extension_path
def quit(self):
"""关闭浏览器"""
if self.browser:
try:
self.browser.quit()
except:
pass

View File

@@ -29,15 +29,19 @@ a = Analysis(
('cursor_auth.py', '.'), ('cursor_auth.py', '.'),
('reset_machine_manual.py', '.'), ('reset_machine_manual.py', '.'),
('cursor_register.py', '.'), ('cursor_register.py', '.'),
('browser.py', '.'), ('new_signup.py', '.'),
('control.py', '.'), ('new_tempemail.py', '.'),
('quit_cursor.py', '.'),
('cursor_register_manual.py', '.'),
('.env', '.') ('.env', '.')
], ],
hiddenimports=[ hiddenimports=[
'cursor_auth', 'cursor_auth',
'reset_machine_manual', 'reset_machine_manual',
'browser', 'new_signup',
'control' 'new_tempemail',
'quit_cursor',
'cursor_register_manual'
], ],
hookspath=[], hookspath=[],
hooksconfig={}, hooksconfig={},

117
config.py Normal file
View File

@@ -0,0 +1,117 @@
import os
import sys
import configparser
from colorama import Fore, Style
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
def setup_config(translator=None):
"""Setup configuration file and return config object"""
try:
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
os.makedirs(config_dir, exist_ok=True)
config = configparser.ConfigParser()
# Default configuration
default_config = {
'Chrome': {
'chromepath': get_default_chrome_path()
},
'Turnstile': {
'handle_turnstile_time': '2',
'handle_turnstile_random_time': '1-3'
},
'Timing': {
'min_random_time': '0.1',
'max_random_time': '0.8',
'page_load_wait': '0.1-0.8',
'input_wait': '0.3-0.8',
'submit_wait': '0.5-1.5',
'verification_code_input': '0.1-0.3',
'verification_success_wait': '2-3',
'verification_retry_wait': '2-3',
'email_check_initial_wait': '4-6',
'email_refresh_wait': '2-4',
'settings_page_load_wait': '1-2',
'failed_retry_time': '0.5-1',
'retry_interval': '8-12',
'max_timeout': '160'
}
}
# Add system-specific path configuration
if sys.platform == "win32":
appdata = os.getenv("APPDATA")
localappdata = os.getenv("LOCALAPPDATA", "")
default_config['WindowsPaths'] = {
'storage_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "storage.json"),
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
'machine_id_path': os.path.join(appdata, "Cursor", "machineId"),
'cursor_path': os.path.join(localappdata, "Programs", "Cursor", "resources", "app"),
'updater_path': os.path.join(localappdata, "cursor-updater")
}
elif sys.platform == "darwin":
default_config['MacPaths'] = {
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app",
'updater_path': os.path.expanduser("~/Library/Application Support/cursor-updater")
}
elif sys.platform == "linux":
sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
default_config['LinuxPaths'] = {
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/.config/Cursor/machineId"),
'cursor_path': get_linux_cursor_path(),
'updater_path': os.path.expanduser("~/.config/cursor-updater")
}
# Read existing configuration and merge
if os.path.exists(config_file):
config.read(config_file, encoding='utf-8')
config_modified = False
for section, options in default_config.items():
if not config.has_section(section):
config.add_section(section)
config_modified = True
for option, value in options.items():
if not config.has_option(section, option):
config.set(section, option, str(value))
config_modified = True
if translator:
print(f"{Fore.YELLOW} {translator.get('register.config_option_added', option=f'{section}.{option}')}{Style.RESET_ALL}")
if config_modified:
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_updated')}{Style.RESET_ALL}")
else:
for section, options in default_config.items():
config.add_section(section)
for option, value in options.items():
config.set(section, option, str(value))
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_created')}: {config_file}{Style.RESET_ALL}")
return config
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.config_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ Error setting up config: {e}{Style.RESET_ALL}")
return None
def get_config(translator=None):
"""Get existing config or create new one"""
return setup_config(translator)

View File

@@ -1,240 +0,0 @@
import time
import random
import os
from colorama import Fore, Style, init
# 初始化colorama
init()
# 定义emoji常量
EMOJI = {
'MAIL': '📧',
'REFRESH': '🔄',
'SUCCESS': '',
'ERROR': '',
'INFO': '',
'CODE': '📱'
}
class BrowserControl:
def __init__(self, browser, translator=None):
self.browser = browser
self.translator = translator # 保存translator
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
self.current_tab = None # 当前标签页
self.signup_tab = None # 注册标签页
self.email_tab = None # 邮箱标签页
def create_new_tab(self):
"""创建新标签页"""
try:
# 保存当前标签页
self.current_tab = self.browser
# 创建新的浏览器实例
from browser import BrowserManager
browser_manager = BrowserManager()
new_browser = browser_manager.init_browser()
# 保存新标签页
self.signup_tab = new_browser
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.create_new_tab_success')}{Style.RESET_ALL}")
return new_browser
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.create_new_tab_failed', error=str(e))}{Style.RESET_ALL}")
return None
def switch_to_tab(self, browser):
"""切换到指定浏览器窗口"""
try:
self.browser = browser
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.switch_tab_success')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.switch_tab_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_current_tab(self):
"""获取当前标签页"""
return self.browser
def wait_for_page_load(self, seconds=2):
"""等待页面加载"""
time.sleep(seconds)
def navigate_to(self, url):
"""导航到指定URL"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.navigate_to', url=url)}...{Style.RESET_ALL}")
self.browser.get(url)
self.wait_for_page_load()
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.browser_error', error=str(e))}{Style.RESET_ALL}")
return False
def get_verification_code(self):
"""从邮件中获取验证码"""
try:
# 尝试所有可能的样式组合
selectors = [
# 新样式
'xpath://div[contains(@style, "font-family:-apple-system") and contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "color:#202020")]',
# 带行高的样式
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px") and contains(@style, "line-height:30px")]',
# rgba 颜色样式
'xpath://div[contains(@style, "font-size: 28px") and contains(@style, "letter-spacing: 2px") and contains(@style, "color: rgba(32, 32, 32, 1)")]',
# 宽松样式
'xpath://div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]'
]
# 依次尝试每个选择器
for selector in selectors:
code_div = self.browser.ele(selector)
if code_div:
verification_code = code_div.text.strip()
if verification_code.isdigit() and len(verification_code) == 6:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.found_verification_code')}: {verification_code}{Style.RESET_ALL}")
return verification_code
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.no_valid_verification_code')}{Style.RESET_ALL}")
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_verification_code_error', error=str(e))}{Style.RESET_ALL}")
return None
def fill_verification_code(self, code):
"""填写验证码"""
try:
if not code or len(code) != 6:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.verification_code_format_error')}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.fill_verification_code')}...{Style.RESET_ALL}")
# 记住当前标签页(邮箱页面)
email_tab = self.browser
# 切换回注册页面标签
self.switch_to_tab(self.signup_tab)
time.sleep(1)
# 输入验证码
for digit in code:
self.browser.actions.input(digit)
time.sleep(random.uniform(0.1, 0.3))
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.verification_code_filled')}{Style.RESET_ALL}")
# 等待页面加载和登录完成
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.wait_for_login')}...{Style.RESET_ALL}")
time.sleep(5)
# 先访问登录页面确保登录状态
login_url = "https://authenticator.cursor.sh"
self.browser.get(login_url)
time.sleep(3) # 增加等待时间
# 获取cookies第一次尝试
token = self.get_cursor_session_token()
if not token:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.get_token_failed')}...{Style.RESET_ALL}")
time.sleep(3)
token = self.get_cursor_session_token()
if token:
self.save_token_to_file(token)
# 获取到token后再访问设置页面
settings_url = "https://www.cursor.com/settings"
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.get_account_info')}...{Style.RESET_ALL}")
self.browser.get(settings_url)
time.sleep(2)
# 获取账户额度信息
try:
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = self.browser.ele(usage_selector)
if usage_ele:
usage_info = usage_ele.text
total_usage = usage_info.split("/")[-1].strip()
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('control.account_usage_limit')}: {total_usage}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_account_usage_failed', error=str(e))}{Style.RESET_ALL}")
# 切换回邮箱页面
self.switch_to_tab(email_tab)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.fill_verification_code_failed', error=str(e))}{Style.RESET_ALL}")
return False
def check_and_click_turnstile(self):
"""检查并点击 Turnstile 验证框"""
try:
# 等待验证框出现
time.sleep(1)
# 查找验证框
verify_checkbox = self.browser.ele('xpath://label[contains(@class, "cb-lb")]//input[@type="checkbox"]')
if verify_checkbox:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.find_turnstile_verification_box')}...{Style.RESET_ALL}")
verify_checkbox.click()
time.sleep(2) # 等待验证完成
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.clicked_turnstile_verification_box')}{Style.RESET_ALL}")
return True
return False
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.check_and_click_turnstile_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_cursor_session_token(self, max_attempts=3, retry_interval=2):
"""获取Cursor会话token"""
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('control.get_cursor_session_token')}...{Style.RESET_ALL}")
attempts = 0
while attempts < max_attempts:
try:
# 直接从浏览器对象获取cookies
all_cookies = self.browser.get_cookies()
# 遍历查找目标cookie
for cookie in all_cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = cookie["value"].split("%3A%3A")[1]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.get_cursor_session_token_success')}: {token}{Style.RESET_ALL}")
return token
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.get_cursor_session_token_failed', attempts=attempts, retry_interval=retry_interval)}...{Style.RESET_ALL}")
time.sleep(retry_interval)
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.reach_max_attempts', max_attempts=max_attempts)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.get_cookie_failed', error=str(e))}{Style.RESET_ALL}")
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['ERROR']} {self.translator.get('control.will_retry_in', retry_interval=retry_interval)}...{Style.RESET_ALL}")
time.sleep(retry_interval)
return None
def save_token_to_file(self, token):
"""保存token到文件"""
try:
with open('cursor_tokens.txt', 'a', encoding='utf-8') as f:
f.write(f"Token: {token}\n")
f.write("-" * 50 + "\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('control.token_saved_to_file')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('control.save_token_failed', error=str(e))}{Style.RESET_ALL}")

View File

@@ -2,11 +2,12 @@ import sqlite3
import os import os
import sys import sys
from colorama import Fore, Style, init from colorama import Fore, Style, init
from config import get_config
# 初始化colorama # Initialize colorama
init() init()
# 定义emoji和颜色常量 # Define emoji and color constants
EMOJI = { EMOJI = {
'DB': '🗄️', 'DB': '🗄️',
'UPDATE': '🔄', 'UPDATE': '🔄',
@@ -21,22 +22,48 @@ EMOJI = {
class CursorAuth: class CursorAuth:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
# 判断操作系统
if os.name == "nt": # Windows
self.db_path = os.path.join(
os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb"
)
else: # macOS
self.db_path = os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
)
# 检查数据库文件是否存在 # Get configuration
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}")
sys.exit(1)
# Get path based on operating system
try:
if sys.platform == "win32": # Windows
if not config.has_section('WindowsPaths'):
raise ValueError("Windows paths not configured")
self.db_path = config.get('WindowsPaths', 'sqlite_path')
elif sys.platform == 'linux': # Linux
if not config.has_section('LinuxPaths'):
raise ValueError("Linux paths not configured")
self.db_path = config.get('LinuxPaths', 'sqlite_path')
elif sys.platform == 'darwin': # macOS
if not config.has_section('MacPaths'):
raise ValueError("macOS paths not configured")
self.db_path = config.get('MacPaths', 'sqlite_path')
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform') if self.translator else 'Unsupported platform'}{Style.RESET_ALL}")
sys.exit(1)
# Verify if the path exists
if not os.path.exists(os.path.dirname(self.db_path)):
raise FileNotFoundError(f"Database directory not found: {os.path.dirname(self.db_path)}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.path_error', error=str(e)) if self.translator else f'Error getting database path: {str(e)}'}{Style.RESET_ALL}")
sys.exit(1)
# Check if the database file exists
if not os.path.exists(self.db_path): 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
@@ -51,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()
@@ -71,31 +98,34 @@ 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"))
if email is not None: if email is not None:
updates.append(("cursorAuth/cachedEmail", email)) updates.append(("cursorAuth/cachedEmail", email))
if access_token is not None: if access_token is not None:
updates.append(("cursorAuth/accessToken", access_token)) updates.append(("cursorAuth/accessToken", access_token))
if refresh_token is not None: if refresh_token is not None:
updates.append(("cursorAuth/refreshToken", refresh_token)) updates.append(("cursorAuth/refreshToken", refresh_token))
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
# 使用事务来确保数据完整性
# 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("""
@@ -127,5 +157,3 @@ class CursorAuth:
if conn: if conn:
conn.close() conn.close()
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}") print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")

View File

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

View File

@@ -2,8 +2,6 @@ import os
from colorama import Fore, Style, init from colorama import Fore, Style, init
import time import time
import random import random
from browser import BrowserManager
from control import BrowserControl
from cursor_auth import CursorAuth from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter from reset_machine_manual import MachineIDResetter
@@ -35,7 +33,6 @@ class CursorRegistration:
self.translator = translator self.translator = translator
# Set to display mode # Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False' os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager()
self.browser = None self.browser = None
self.controller = None self.controller = None
self.sign_up_url = "https://authenticator.cursor.sh/sign-up" self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
@@ -44,22 +41,33 @@ class CursorRegistration:
self.signup_tab = None self.signup_tab = None
self.email_tab = None self.email_tab = None
# Account information # Generate account information
self.password = self._generate_password() self.password = self._generate_password()
self.first_name = self._generate_name() # Generate first name and last name separately
self.last_name = self._generate_name() first_name = random.choice([
"James", "John", "Robert", "Michael", "William", "David", "Joseph", "Thomas",
"Emma", "Olivia", "Ava", "Isabella", "Sophia", "Mia", "Charlotte", "Amelia",
"Liam", "Noah", "Oliver", "Elijah", "Lucas", "Mason", "Logan", "Alexander"
])
self.last_name = random.choice([
"Smith", "Johnson", "Williams", "Brown", "Jones", "Garcia", "Miller", "Davis",
"Anderson", "Wilson", "Taylor", "Thomas", "Moore", "Martin", "Jackson", "Lee",
"Thompson", "White", "Harris", "Clark", "Lewis", "Walker", "Hall", "Young"
])
# Modify first letter of first name
new_first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
self.first_name = new_first_letter + first_name[1:]
print(f"\n{Fore.CYAN}{EMOJI['PASSWORD']} {self.translator.get('register.password')}: {self.password} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.first_name')}: {self.first_name} {Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['FORM']} {self.translator.get('register.last_name')}: {self.last_name} {Style.RESET_ALL}")
def _generate_password(self, length=12): def _generate_password(self, length=12):
"""Generate Random Password""" """Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*" chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length)) return ''.join(random.choices(chars, k=length))
def _generate_name(self, length=6):
"""Generate Random Name"""
first_letter = random.choice("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
rest_letters = ''.join(random.choices("abcdefghijklmnopqrstuvwxyz", k=length-1))
return first_letter + rest_letters
def setup_email(self): def setup_email(self):
"""Setup Email""" """Setup Email"""
try: try:
@@ -70,6 +78,7 @@ class CursorRegistration:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}")
return True return True
except Exception as e: except Exception as e:
@@ -155,6 +164,7 @@ class CursorRegistration:
if usage_ele: if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip() total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30 max_attempts = 30
retry_interval = 2 retry_interval = 2

View File

@@ -4,6 +4,7 @@ import platform
import shutil import shutil
from colorama import Fore, Style, init from colorama import Fore, Style, init
import subprocess import subprocess
from config import get_config
# Initialize colorama # Initialize colorama
init() init()
@@ -24,11 +25,24 @@ class AutoUpdateDisabler:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
self.system = platform.system() self.system = platform.system()
# Get path from configuration file
config = get_config(translator)
if config:
if self.system == "Windows":
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
elif self.system == "Darwin":
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
elif self.system == "Linux":
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
else:
# If configuration loading fails, use default paths
self.updater_paths = { 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"),
"Linux": os.path.expanduser("~/.config/cursor-updater") "Linux": os.path.expanduser("~/.config/cursor-updater")
} }
self.updater_path = self.updater_paths.get(self.system)
def _kill_cursor_processes(self): def _kill_cursor_processes(self):
"""End all Cursor processes""" """End all Cursor processes"""
@@ -50,7 +64,7 @@ class AutoUpdateDisabler:
def _remove_updater_directory(self): def _remove_updater_directory(self):
"""Delete updater directory""" """Delete updater directory"""
try: try:
updater_path = self.updater_paths.get(self.system) updater_path = self.updater_path
if not updater_path: if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}") raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
@@ -72,7 +86,7 @@ class AutoUpdateDisabler:
def _create_blocking_file(self): def _create_blocking_file(self):
"""Create blocking file""" """Create blocking file"""
try: try:
updater_path = self.updater_paths.get(self.system) updater_path = self.updater_path
if not updater_path: if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}") raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -17,7 +17,8 @@
"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",
@@ -68,7 +69,24 @@
"version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId", "version_less_than_0_45": "Cursor Version < 0.45.0, Skip Patching getMachineId",
"detecting_version": "Detecting Cursor Version", "detecting_version": "Detecting Cursor Version",
"patching_getmachineid": "Patching getMachineId", "patching_getmachineid": "Patching getMachineId",
"version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId" "version_greater_than_0_45": "Cursor Version >= 0.45.0, Patching getMachineId",
"permission_denied": "Permission Denied: {error}",
"backup_created": "Backup Created",
"update_success": "Update Success",
"update_failed": "Update Failed: {error}",
"windows_machine_guid_updated": "Windows Machine GUID Updated Successfully",
"reading_package_json": "Reading package.json {path}",
"invalid_json_object": "Invalid JSON Object",
"no_version_field": "No Version Field Found in package.json",
"version_field_empty": "Version Field is Empty",
"invalid_version_format": "Invalid Version Format: {version}",
"found_version": "Found Version: {version}",
"version_parse_error": "Version Parse Error: {error}",
"package_not_found": "Package.json Not Found: {path}",
"check_version_failed": "Check Version Failed: {error}",
"stack_trace": "Stack Trace",
"version_too_low": "Cursor Version Too Low: {version} < 0.45.0"
}, },
"register": { "register": {
"title": "Cursor Registration Tool", "title": "Cursor Registration Tool",
@@ -131,7 +149,21 @@
"register_process_error": "Register Process Error: {error}", "register_process_error": "Register Process Error: {error}",
"setting_password": "Setting Password", "setting_password": "Setting Password",
"manual_code_input": "Manual Code Input", "manual_code_input": "Manual Code Input",
"manual_email_input": "Manual Email Input" "manual_email_input": "Manual Email Input",
"password": "Password",
"first_name": "First Name",
"last_name": "Last Name",
"exit_signal": "Exit Signal",
"email_address": "Email Address",
"config_created": "Config Created",
"verification_failed": "Verification Failed",
"verification_error": "Verification Error: {error}",
"config_option_added": "Config Option Added: {option}",
"config_updated": "Config Updated",
"password_submitted": "Password Submitted",
"total_usage": "Total Usage: {usage}",
"setting_on_password": "Setting Password",
"getting_code": "Getting Verification Code, Will Try in 60s"
}, },
"auth": { "auth": {
"title": "Cursor Auth Manager", "title": "Cursor Auth Manager",
@@ -203,7 +235,24 @@
"verification_code_found": "Verification Code Found", "verification_code_found": "Verification Code Found",
"verification_code_not_found": "Verification Code Not Found", "verification_code_not_found": "Verification Code Not Found",
"verification_code_error": "Verification Code Error: {error}", "verification_code_error": "Verification Code Error: {error}",
"address": "Email Address" "address": "Email Address",
"all_domains_blocked": "All Domains Blocked, Switching Service",
"no_available_domains_after_filtering": "No Available Domains After Filtering",
"switching_service": "Switching to {service} Service",
"domains_list_error": "Failed to Get Domains List: {error}",
"failed_to_get_available_domains": "Failed to Get Available Domains",
"domains_excluded": "Domains Excluded: {domains}",
"failed_to_create_account": "Failed to Create Account",
"account_creation_error": "Account Creation Error: {error}",
"blocked_domains": "Blocked Domains: {domains}",
"blocked_domains_loaded": "Blocked Domains Loaded: {count}",
"blocked_domains_loaded_error": "Blocked Domains Loaded Error: {error}",
"blocked_domains_loaded_success": "Blocked Domains Loaded Successfully",
"blocked_domains_loaded_timeout": "Blocked Domains Loaded Timeout: {timeout}s",
"blocked_domains_loaded_timeout_error": "Blocked Domains Loaded Timeout Error: {error}",
"available_domains_loaded": "Available Domains Loaded: {count}",
"domains_filtered": "Domains Filtered: {count}",
"trying_to_create_email": "Trying to create email: {email}"
}, },
"update": { "update": {
"title": "Disable Cursor Auto Update", "title": "Disable Cursor Auto Update",
@@ -217,5 +266,16 @@
"directory_removed": "Directory Removed", "directory_removed": "Directory Removed",
"creating_block_file": "Creating Block File", "creating_block_file": "Creating Block File",
"block_file_created": "Block File Created" "block_file_created": "Block File Created"
},
"updater": {
"checking": "Checking for updates...",
"new_version_available": "New version available! (Current: {current}, Latest: {latest})",
"updating": "Updating to the latest version. The program will restart automatically.",
"up_to_date": "You are using the latest version.",
"check_failed": "Failed to check for updates: {error}",
"continue_anyway": "Continuing with current version...",
"update_confirm": "Do you want to update to the latest version? (Y/n)",
"update_skipped": "Skipping update.",
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'."
} }
} }

281
locales/vi.json Normal file
View File

@@ -0,0 +1,281 @@
{
"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"
},
"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'."
}
}

View File

@@ -17,7 +17,8 @@
"languages": { "languages": {
"en": "English", "en": "English",
"zh_cn": "简体中文", "zh_cn": "简体中文",
"zh_tw": "繁體中文" "zh_tw": "繁體中文",
"vi": "Vietnamese"
}, },
"quit_cursor": { "quit_cursor": {
"start": "开始退出 Cursor", "start": "开始退出 Cursor",
@@ -68,7 +69,23 @@
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补", "version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "检测Cursor版本", "detecting_version": "检测Cursor版本",
"patching_getmachineid": "修补getMachineId", "patching_getmachineid": "修补getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修补getMachineId" "version_greater_than_0_45": "Cursor版本 >= 0.45.0修补getMachineId",
"permission_denied": "权限拒绝: {error}",
"backup_created": "备份已创建",
"update_success": "更新成功",
"update_failed": "更新失败: {error}",
"windows_machine_guid_updated": "Windows机器GUID更新成功",
"reading_package_json": "读取package.json {path}",
"invalid_json_object": "JSON对象无效",
"no_version_field": "package.json中没有版本字段",
"version_field_empty": "版本字段为空",
"invalid_version_format": "版本格式无效: {version}",
"found_version": "找到版本: {version}",
"version_parse_error": "版本解析错误: {error}",
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "检查版本失败: {error}",
"stack_trace": "堆栈跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
}, },
"register": { "register": {
"title": "Cursor 注册工具", "title": "Cursor 注册工具",
@@ -131,7 +148,21 @@
"update_cursor_auth_info": "更新Cursor认证信息", "update_cursor_auth_info": "更新Cursor认证信息",
"setting_password": "设置密码", "setting_password": "设置密码",
"manual_code_input": "手动输入验证码", "manual_code_input": "手动输入验证码",
"manual_email_input": "手动输入邮箱" "manual_email_input": "手动输入邮箱",
"password": "密码",
"first_name": "名字",
"last_name": "姓氏",
"exit_signal": "退出信号",
"email_address": "邮箱地址",
"config_created": "配置已创建",
"verification_failed": "验证失败",
"verification_error": "验证错误: {error}",
"config_option_added": "配置项已添加: {option}",
"config_updated": "配置已更新",
"password_submitted": "密码已提交",
"total_usage": "总使用量: {usage}",
"setting_on_password": "设置密码",
"getting_code": "获取验证码将在60秒内尝试..."
}, },
"auth": { "auth": {
"title": "Cursor 认证管理器", "title": "Cursor 认证管理器",
@@ -200,7 +231,24 @@
"verification_code_found": "找到验证码", "verification_code_found": "找到验证码",
"verification_code_not_found": "未找到验证码", "verification_code_not_found": "未找到验证码",
"verification_code_error": "验证码错误: {error}", "verification_code_error": "验证码错误: {error}",
"address": "邮箱地址" "address": "邮箱地址",
"all_domains_blocked": "所有域名都被屏蔽了,切换服务",
"no_available_domains_after_filtering": "过滤后没有可用域名",
"switching_service": "切换到 {service} 服务",
"domains_list_error": "获取域名列表失败: {error}",
"failed_to_get_available_domains": "获取可用域名失败",
"blocked_domains_loaded": "加载了 {count} 个被屏蔽的域名",
"domains_excluded": "排除了 {domains} 个被屏蔽的域名",
"failed_to_create_account": "创建账户失败",
"account_creation_error": "账户创建错误: {error}",
"blocked_domains": "被屏蔽的域名: {domains}",
"blocked_domains_loaded_error": "加载被屏蔽的域名失败: {error}",
"blocked_domains_loaded_success": "加载被屏蔽的域名成功",
"blocked_domains_loaded_timeout": "加载被屏蔽的域名超时: {timeout}秒",
"blocked_domains_loaded_timeout_error": "加载被屏蔽的域名超时错误: {error}",
"available_domains_loaded": "获取到 {count} 个可用域名",
"domains_filtered": "过滤后剩餘 {count} 個可用域名",
"trying_to_create_email": "尝试创建邮箱: {email}"
}, },
"update": { "update": {
"title": "禁用 Cursor 自动更新", "title": "禁用 Cursor 自动更新",
@@ -214,5 +262,16 @@
"directory_removed": "目录已删除", "directory_removed": "目录已删除",
"creating_block_file": "创建阻止文件", "creating_block_file": "创建阻止文件",
"block_file_created": "阻止文件已创建" "block_file_created": "阻止文件已创建"
},
"updater": {
"checking": "检查更新...",
"new_version_available": "有新版本可用! (当前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序将自动重启。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "检查更新失败: {error}",
"continue_anyway": "继续使用当前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳过更新。",
"invalid_choice": "选择无效。请输入 'Y' 或 'n'."
} }
} }

View File

@@ -17,7 +17,8 @@
"languages": { "languages": {
"en": "English", "en": "English",
"zh_cn": "简体中文", "zh_cn": "简体中文",
"zh_tw": "繁體中文" "zh_tw": "繁體中文",
"vi": "Vietnamese"
}, },
"quit_cursor": { "quit_cursor": {
"start": "開始退出 Cursor", "start": "開始退出 Cursor",
@@ -68,8 +69,25 @@
"version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补", "version_less_than_0_45": "Cursor版本 < 0.45.0跳过getMachineId修补",
"detecting_version": "檢測Cursor版本", "detecting_version": "檢測Cursor版本",
"patching_getmachineid": "修補getMachineId", "patching_getmachineid": "修補getMachineId",
"version_greater_than_0_45": "Cursor版本 >= 0.45.0修補getMachineId" "version_greater_than_0_45": "Cursor版本 >= 0.45.0修補getMachineId",
"permission_denied": "權限拒絕: {error}",
"backup_created": "備份已創建",
"update_success": "更新成功",
"update_failed": "更新失敗: {error}",
"windows_machine_guid_updated": "Windows機器GUID更新成功",
"reading_package_json": "讀取package.json {path}",
"invalid_json_object": "JSON對象無效",
"no_version_field": "package.json中沒有版本字段",
"version_field_empty": "版本字段為空",
"invalid_version_format": "版本格式無效: {version}",
"found_version": "找到版本: {version}",
"version_parse_error": "版本解析錯誤: {error}",
"package_not_found": "package.json未找到: {path}",
"check_version_failed": "檢查版本失敗: {error}",
"stack_trace": "堆疊跟踪",
"version_too_low": "Cursor版本太低: {version} < 0.45.0"
}, },
"register": { "register": {
"title": "Cursor 註冊工具", "title": "Cursor 註冊工具",
"start": "正在啟動註冊流程...", "start": "正在啟動註冊流程...",
@@ -112,7 +130,21 @@
"update_cursor_auth_info": "更新Cursor認證信息", "update_cursor_auth_info": "更新Cursor認證信息",
"setting_password": "設置密碼", "setting_password": "設置密碼",
"manual_code_input": "手動輸入驗證碼", "manual_code_input": "手動輸入驗證碼",
"manual_email_input": "手動輸入郵箱地址" "manual_email_input": "手動輸入郵箱地址",
"password": "密碼",
"first_name": "名字",
"last_name": "姓氏",
"exit_signal": "退出信號",
"email_address": "郵箱地址",
"config_created": "配置已創建",
"verification_failed": "驗證失敗",
"verification_error": "驗證錯誤: {error}",
"config_option_added": "配置項已添加: {option}",
"config_updated": "配置已更新",
"password_submitted": "密碼已提交",
"total_usage": "總使用量: {usage}",
"setting_on_password": "設置密碼",
"getting_code": "正在獲取驗證碼將在60秒內嘗試..."
}, },
"auth": { "auth": {
"title": "Cursor 認證管理器", "title": "Cursor 認證管理器",
@@ -181,7 +213,24 @@
"verification_code_found": "找到驗證碼", "verification_code_found": "找到驗證碼",
"verification_code_not_found": "未找到驗證碼", "verification_code_not_found": "未找到驗證碼",
"verification_code_error": "驗證碼錯誤: {error}", "verification_code_error": "驗證碼錯誤: {error}",
"address": "郵箱地址" "address": "郵箱地址",
"all_domains_blocked": "所有域名都被屏蔽了,切換服務",
"no_available_domains_after_filtering": "過濾後沒有可用域名",
"switching_service": "切換到 {service} 服務",
"domains_list_error": "獲取域名列表失敗: {error}",
"failed_to_get_available_domains": "獲取可用域名失敗",
"domains_excluded": "排除的域名: {domains}",
"failed_to_create_account": "創建帳戶失敗",
"account_creation_error": "帳戶創建錯誤: {error}",
"blocked_domains": "被屏蔽的域名: {domains}",
"blocked_domains_loaded": "加載被屏蔽的域名: {domains}",
"blocked_domains_loaded_error": "加載被屏蔽的域名失敗: {error}",
"blocked_domains_loaded_success": "加載被屏蔽的域名成功",
"blocked_domains_loaded_timeout": "加載被屏蔽的域名超時: {timeout}秒",
"blocked_domains_loaded_timeout_error": "加載被屏蔽的域名超時錯誤: {error}",
"available_domains_loaded": "獲取到 {count} 個可用域名",
"domains_filtered": "過濾後剩餘 {count} 個可用域名",
"trying_to_create_email": "嘗試創建郵箱: {email}"
}, },
"update": { "update": {
"title": "禁用 Cursor 自动更新", "title": "禁用 Cursor 自动更新",
@@ -195,5 +244,16 @@
"directory_removed": "目錄已刪除", "directory_removed": "目錄已刪除",
"creating_block_file": "創建阻止文件", "creating_block_file": "創建阻止文件",
"block_file_created": "阻止文件已創建" "block_file_created": "阻止文件已創建"
},
"updater": {
"checking": "檢查更新...",
"new_version_available": "有新版本可用! (當前版本: {current}, 最新版本: {latest})",
"updating": "正在更新到最新版本。程序將自動重啟。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "檢查更新失敗: {error}",
"continue_anyway": "繼續使用當前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳過更新。",
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'."
} }
} }

184
main.py
View File

@@ -3,21 +3,24 @@
import os import os
import sys import sys
import json import json
from logo import print_logo from logo import print_logo, version
from colorama import Fore, Style, init from colorama import Fore, Style, init
import locale import locale
import platform import platform
import requests
import subprocess
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": "💾",
@@ -28,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()
@@ -55,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)
@@ -70,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')
@@ -93,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()
@@ -100,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:
@@ -165,11 +209,11 @@ 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')}")
@@ -203,8 +247,113 @@ def select_language():
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
return False return False
def check_latest_version():
"""Check if current version matches the latest release version"""
try:
print(f"\n{Fore.CYAN}{EMOJI['UPDATE']} {translator.get('updater.checking')}{Style.RESET_ALL}")
# Get latest version from GitHub API with timeout and proper headers
headers = {
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'CursorFreeVIP-Updater'
}
response = requests.get(
"https://api.github.com/repos/yeongpin/cursor-free-vip/releases/latest",
headers=headers,
timeout=10
)
# Check if response is successful
if response.status_code != 200:
raise Exception(f"GitHub API returned status code {response.status_code}")
response_data = response.json()
if "tag_name" not in response_data:
raise Exception("No version tag found in GitHub response")
latest_version = response_data["tag_name"].lstrip('v')
# Validate version format
if not latest_version:
raise Exception("Invalid version format received")
if latest_version != version:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.new_version_available', current=version, latest=latest_version)}{Style.RESET_ALL}")
# Ask user if they want to update
while True:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('updater.update_confirm', choices='Y/n')}: {Style.RESET_ALL}").lower()
if choice in ['', 'y', 'yes']:
break
elif choice in ['n', 'no']:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.update_skipped')}{Style.RESET_ALL}")
return
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
try:
# Execute update command based on platform
if platform.system() == 'Windows':
update_command = 'irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex'
subprocess.run(['powershell', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', update_command], check=True)
else:
# For Linux/Mac, download and execute the install script
install_script_url = 'https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh'
# First verify the script exists
script_response = requests.get(install_script_url, timeout=5)
if script_response.status_code != 200:
raise Exception("Installation script not found")
# Save and execute the script
with open('install.sh', 'wb') as f:
f.write(script_response.content)
os.chmod('install.sh', 0o755) # Make executable
subprocess.run(['./install.sh'], check=True)
# Clean up
if os.path.exists('install.sh'):
os.remove('install.sh')
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.updating')}{Style.RESET_ALL}")
sys.exit(0)
except Exception as update_error:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.update_failed', error=str(update_error))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.manual_update_required')}{Style.RESET_ALL}")
return
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('updater.up_to_date')}{Style.RESET_ALL}")
except requests.exceptions.RequestException as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.network_error', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.continue_anyway')}{Style.RESET_ALL}")
return
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('updater.check_failed', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.continue_anyway')}{Style.RESET_ALL}")
return
def main(): def main():
# 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)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
return
check_latest_version() # Add version check before showing menu
print_menu() print_menu()
while True: while True:
@@ -218,19 +367,19 @@ def main():
elif choice == "1": elif choice == "1":
import reset_machine_manual import reset_machine_manual
reset_machine_manual.run(translator) reset_machine_manual.run(translator)
break print_menu()
elif choice == "2": elif choice == "2":
import cursor_register import cursor_register
cursor_register.main(translator) cursor_register.main(translator)
break print_menu()
elif choice == "3": elif choice == "3":
import cursor_register_manual import cursor_register_manual
cursor_register_manual.main(translator) cursor_register_manual.main(translator)
break print_menu()
elif choice == "4": elif choice == "4":
import quit_cursor import quit_cursor
quit_cursor.quit_cursor(translator) quit_cursor.quit_cursor(translator)
break print_menu()
elif choice == "5": elif choice == "5":
if select_language(): if select_language():
print_menu() print_menu()
@@ -238,7 +387,7 @@ def main():
elif choice == "6": elif choice == "6":
import disable_auto_update import disable_auto_update
disable_auto_update.run(translator) disable_auto_update.run(translator)
break print_menu()
else: else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu() print_menu()
@@ -249,10 +398,7 @@ def main():
return return
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.error_occurred', error=str(e))}{Style.RESET_ALL}")
break print_menu()
print(f"\n{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('menu.press_enter')}...{Style.RESET_ALL}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@@ -4,13 +4,17 @@ import os
import signal import signal
import random import random
from colorama import Fore, Style from colorama import Fore, Style
import configparser
from pathlib import Path
import sys
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')
@@ -25,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}")
@@ -34,81 +38,164 @@ def signal_handler(signum, frame):
cleanup_chrome_processes(_translator) cleanup_chrome_processes(_translator)
os._exit(0) os._exit(0)
def simulate_human_input(page, url, 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}")
else:
print("正在访问网址...")
# 先访问空白页面 # First visit blank page
page.get('about:blank') page.get('about:blank')
time.sleep(random.uniform(1.0, 2.0)) time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 访问目标页面 # Visit target page
page.get(url) page.get(url)
time.sleep(random.uniform(2.0, 3.0)) # 等待页面加载 time.sleep(get_random_wait_time(config, 'page_load_wait'))
def fill_signup_form(page, first_name, last_name, email, 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(random.uniform(0.5, 1.0)) 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(random.uniform(0.5, 1.0)) 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(random.uniform(0.5, 1.0)) 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()
time.sleep(random.uniform(2.0, 3.0)) time.sleep(get_random_wait_time(config, 'submit_wait'))
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():
"""Get default Chrome path"""
if sys.platform == "win32":
paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google/Chrome/Application/chrome.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google/Chrome/Application/chrome.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google/Chrome/Application/chrome.exe')
]
elif sys.platform == "darwin":
paths = [
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
]
else: # Linux
paths = [
"/usr/bin/google-chrome",
"/usr/bin/google-chrome-stable"
]
for path in paths:
if os.path.exists(path):
return path
return ""
def get_user_documents_path():
"""Get user Documents folder path"""
if sys.platform == "win32":
return os.path.join(os.path.expanduser("~"), "Documents")
elif sys.platform == "darwin":
return os.path.join(os.path.expanduser("~"), "Documents")
else: # Linux
# Get actual user's home directory
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
return os.path.join("/home", sudo_user, "Documents")
return os.path.join(os.path.expanduser("~"), "Documents")
def get_random_wait_time(config, timing_type='page_load_wait'):
"""
Get random wait time from config
Args:
config: ConfigParser object
timing_type: Type of timing to get (page_load_wait, input_wait, submit_wait)
Returns:
float: Random wait time or fixed time
"""
try:
if not config.has_section('Timing'):
return random.uniform(0.1, 0.8) # Default value
if timing_type == 'random':
min_time = float(config.get('Timing', 'min_random_time', fallback='0.1'))
max_time = float(config.get('Timing', 'max_random_time', fallback='0.8'))
return random.uniform(min_time, max_time)
time_value = config.get('Timing', timing_type, fallback='0.1-0.8')
# Check if it's a fixed time value
if '-' not in time_value and ',' not in time_value:
return float(time_value) # Return fixed time
# Process range time
min_time, max_time = map(float, time_value.split('-' if '-' in time_value else ','))
return random.uniform(min_time, max_time)
except:
return random.uniform(0.1, 0.8) # Return default value when error
def setup_driver(translator=None): def setup_driver(translator=None):
"""设置浏览器驱动""" """Setup browser driver"""
try:
# Get config
config = get_config(translator)
# Get Chrome path
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
if not chrome_path or not os.path.exists(chrome_path):
if translator:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.chrome_path_invalid') if translator else 'Chrome路径无效使用默认路径'}{Style.RESET_ALL}")
chrome_path = get_default_chrome_path()
# Set browser options
co = ChromiumOptions() co = ChromiumOptions()
# 使用无痕模式 # Set Chrome path
co.set_browser_path(chrome_path)
# Use incognito mode
co.set_argument("--incognito") co.set_argument("--incognito")
# 设置随机端口 # Set random port
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")
@@ -117,23 +204,40 @@ 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 page except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.browser_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error setting up browser: {e}")
raise
def handle_turnstile(page, 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
turnstile_time = float(config.get('Turnstile', 'handle_turnstile_time', fallback='2'))
random_time_str = config.get('Turnstile', 'handle_turnstile_random_time', fallback='1-3')
# Parse random time range
try:
min_time, max_time = map(float, random_time_str.split('-'))
except:
min_time, max_time = 1, 3 # Default value
max_retries = 2 max_retries = 2
retry_count = 0 retry_count = 0
@@ -143,14 +247,14 @@ def handle_turnstile(page, 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(2) 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()
@@ -163,61 +267,61 @@ def handle_turnstile(page, 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
time.sleep(random.uniform(1, 3)) time.sleep(random.uniform(min_time, max_time))
challenge_check.click() challenge_check.click()
time.sleep(2) time.sleep(turnstile_time) # from config
# 检查验证结果 # check verification result
if check_verification_success(page, translator): if check_verification_success(page, translator):
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
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(1, 2)) time.sleep(random.uniform(min_time, max_time))
if translator: if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
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")]',
@@ -233,114 +337,103 @@ 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, translator=None): def fill_password(page, password: str, config, translator=None):
"""填写密码""" """
Fill password form
"""
try: try:
if translator: print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else 'Setting password'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password')}{Style.RESET_ALL}")
else: # Fill password
print(f"\n{translator.get('register.setting_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}")
if password_input: if password_input:
password_input.input(password) password_input.input(password)
time.sleep(random.uniform(0.5, 1.0))
# 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(random.uniform(2.0, 3.0)) time.sleep(get_random_wait_time(config, 'submit_wait'))
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else 'Password submitted'}{Style.RESET_ALL}")
if translator:
print(f"{Fore.GREEN}{translator.get('register.password_success')}{Style.RESET_ALL}")
else:
print(f"{translator.get('register.password_success')}: {password}")
return True return True
except Exception as e: except Exception as e:
if translator: print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'Error setting password: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{translator.get('register.password_error')}: {e}")
return False return False
def handle_verification_code(browser_tab, email_tab, controller, email, password, translator=None): def handle_verification_code(browser_tab, email_tab, controller, 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}")
else:
print(f"\n{translator.get('register.waiting_for_verification_code')}")
# 检查是否使用手动输入验证码 # 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(random.uniform(0.1, 0.3)) time.sleep(get_random_wait_time(config, 'verification_code_input'))
print(f"{translator.get('register.verification_success')}") print(f"{translator.get('register.verification_success')}")
time.sleep(3) time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# 处理最后一次 Turnstile 验证 # Handle last Turnstile verification
if handle_turnstile(browser_tab, translator): if handle_turnstile(browser_tab, config, translator):
if translator: if translator:
print(f"{translator.get('register.verification_success')}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else: time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
print(f"{translator.get('register.verification_success')}")
time.sleep(2)
# 访问设置页面 # Visit settings page
print(f"{translator.get('register.visiting_url')}: https://www.cursor.com/settings") print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab return True, browser_tab
return False, None return False, None
# 自动获取验证码逻辑 # Automatic verification code logic
elif email_tab: elif email_tab:
print(f"{translator.get('register.waiting_for_verification_code')}") print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
time.sleep(5) # 等待验证码邮件 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(3) 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(random.uniform(0.1, 0.3)) time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else: time.sleep(get_random_wait_time(config, 'verification_success_wait'))
print("验证码填写完成")
time.sleep(3)
# 处理最后一次 Turnstile 验证 # Handle last Turnstile verification
if handle_turnstile(browser_tab, 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}")
else: time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
print("最后一次验证通过!")
time.sleep(2)
# 访问设置页面 # 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}")
else:
print("访问设置页面...")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab return True, browser_tab
else: else:
@@ -350,81 +443,65 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
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 = 10 retry_interval = get_random_wait_time(config, 'retry_interval') # Use get_random_wait_time
start_time = time.time() start_time = time.time()
timeout = 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}")
else:
print("开始获取验证码...")
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}")
else:
print("获取验证码超时...")
break break
verification_code = controller.get_verification_code() verification_code = controller.get_verification_code()
if verification_code: if verification_code:
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print(f"成功获取验证码: {verification_code}")
break break
remaining_time = int(timeout - (time.time() - start_time)) remaining_time = int(timeout - (time.time() - start_time))
if translator: if translator:
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}") print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
else:
print(f"{attempt + 1} 次尝试获取验证码,剩余时间: {remaining_time}秒...")
# 刷新邮箱 # Refresh email
email_tab.refresh_inbox() email_tab.refresh_inbox()
time.sleep(retry_interval) 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(random.uniform(0.1, 0.3)) time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator: if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}") print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else: time.sleep(get_random_wait_time(config, 'verification_success_wait'))
print("验证码填写完成")
time.sleep(3)
# 处理最后一次 Turnstile 验证 # Handle last Turnstile verification
if handle_turnstile(browser_tab, 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}")
else: time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
print("最后一次验证通过!")
time.sleep(2)
# 直接访问设置页面 # 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}")
else:
print("访问设置页面...")
browser_tab.get("https://www.cursor.com/settings") browser_tab.get("https://www.cursor.com/settings")
time.sleep(3) # 等待页面加载 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:
if translator: if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("最后一次验证失败")
return False, None return False, None
return False, None return False, None
@@ -432,63 +509,61 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
except Exception as e: except Exception as e:
if translator: if translator:
print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"处理验证码时出错: {e}")
return False, None return False, None
def handle_sign_in(browser_tab, email, password, translator=None): def handle_sign_in(browser_tab, email, password, translator=None):
"""处理登录流程""" """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)
@@ -496,80 +571,63 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
page = None page = None
success = False success = False
try: try:
page = setup_driver(translator) config, page = setup_driver(translator)
if translator: if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}") print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}")
else:
print("浏览器已启动")
# 访问注册页面 # Visit registration page
url = "https://authenticator.cursor.sh/sign-up" url = "https://authenticator.cursor.sh/sign-up"
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
else:
print(f"\n正在访问: {url}")
# 访问页面 # Visit page
simulate_human_input(page, url, 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}")
else: time.sleep(get_random_wait_time(config, 'page_load_wait'))
print("等待页面加载...")
time.sleep(5)
# 如果没有提供账号信息,则生成随机信息 # 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, 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}")
else:
print("\n表单已提交,开始验证...")
# 处理第一次 Turnstile 验证 # Handle first Turnstile verification
if handle_turnstile(page, translator): if handle_turnstile(page, config, translator):
if translator: if translator:
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}") print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
else:
print("\n第一阶段验证通过!")
# 填写密码 # Fill password
if fill_password(page, password, 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}")
else:
print("\n等待第二次验证...")
time.sleep(2)
# 处理第二次 Turnstile 验证 # Handle second Turnstile verification
if handle_turnstile(page, translator): if handle_turnstile(page, config, translator):
if translator: if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
else: if handle_verification_code(page, email_tab, controller, config, translator):
print("\n开始处理验证码...")
if handle_verification_code(page, email_tab, controller, email, password, translator):
success = True success = True
return True, page # 返回浏览器实例 return True, page
else: else:
print("\n验证码处理失败") print(f"\n{Fore.RED}{translator.get('register.verification_code_processing_failed') if translator else 'Verification code processing failed'}{Style.RESET_ALL}")
else: else:
print("\n第二次验证失败") print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else: else:
print("\n密码设置失败") print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else: else:
print("\n第一次验证失败") 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
@@ -577,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:
@@ -585,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

@@ -7,7 +7,7 @@ import requests
import random import random
import string import string
# 初始化 colorama # Initialize colorama
init() init()
class NewTempEmail: class NewTempEmail:
@@ -23,57 +23,191 @@ class NewTempEmail:
self.token = None self.token = None
self.email = None self.email = None
self.password = None self.password = None
self.blocked_domains = self.get_blocked_domains()
def get_blocked_domains(self):
"""Get blocked domains list"""
try:
block_url = "https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/block_domain.txt"
response = requests.get(block_url, timeout=5)
if response.status_code == 200:
# Split text and remove empty lines
domains = [line.strip() for line in response.text.split('\n') if line.strip()]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 已加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
return domains
return []
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.blocked_domains_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 获取被屏蔽域名列表失败: {str(e)}{Style.RESET_ALL}")
return []
def exclude_blocked_domains(self, domains):
"""Exclude blocked domains"""
if not self.blocked_domains:
return domains
filtered_domains = []
for domain in domains:
if domain['domain'] not in self.blocked_domains:
filtered_domains.append(domain)
excluded_count = len(domains) - len(filtered_domains)
if excluded_count > 0:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domains_excluded', domains=excluded_count)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 已排除 {excluded_count} 个被屏蔽的域名{Style.RESET_ALL}")
return filtered_domains
def _generate_credentials(self): def _generate_credentials(self):
"""生成随机用户名和密码""" """generate random username and password"""
username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10)) username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=12)) password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=12))
return username, password return username, password
def create_email(self): def create_email(self):
"""创建临时邮箱""" """create temporary email"""
max_retries = 3 # Maximum number of retries
attempt = 0 # Current attempt count
while attempt < max_retries:
attempt += 1
try: try:
if self.translator: if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.visiting_site').replace('mail.tm', self.selected_service['name'])}{Style.RESET_ALL}") print(f"{Fore.CYAN} {self.translator.get('email.visiting_site').replace('mail.tm', self.selected_service['name'])}{Style.RESET_ALL}")
else: else:
print(f"{Fore.CYAN} 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}") print(f"{Fore.CYAN} 正在访问 {self.selected_service['name']}...{Style.RESET_ALL}")
# 获取可用域名列表 # Get available domain list
domains_response = requests.get(f"{self.api_url}/domains") try:
domains_response = requests.get(f"{self.api_url}/domains", timeout=10)
if domains_response.status_code != 200: if domains_response.status_code != 200:
raise Exception(f"{self.translator.get('email.failed_to_get_available_domains')}") print(f"{Fore.RED}{self.translator.get('email.domains_list_error', error=domains_response.status_code)}{Style.RESET_ALL}")
print(f"{Fore.RED}{self.translator.get('email.domains_list_error', error=domains_response.text)}{Style.RESET_ALL}")
raise Exception(f"{self.translator.get('email.failed_to_get_available_domains') if self.translator else 'Failed to get available domains'}")
domains = domains_response.json()["hydra:member"] domains = domains_response.json()["hydra:member"]
if not domains: print(f"{Fore.CYAN} {self.translator.get('email.available_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
raise Exception(f"{self.translator.get('email.no_available_domains')}")
# 生成随机用户名和密码 if not domains:
raise Exception(f"{self.translator.get('email.no_available_domains') if self.translator else '没有可用域名'}")
except Exception as e:
print(f"{Fore.RED}❌ 获取域名列表时出错: {str(e)}{Style.RESET_ALL}")
raise
# Exclude blocked domains
try:
filtered_domains = self.exclude_blocked_domains(domains)
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.domains_filtered', count=len(filtered_domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 过滤后剩余 {len(filtered_domains)} 个可用域名{Style.RESET_ALL}")
if not filtered_domains:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.all_domains_blocked')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 所有域名都被屏蔽了,尝试切换服务{Style.RESET_ALL}")
# Switch to another service
for service in self.services:
if service["api_url"] != self.api_url:
self.selected_service = service
self.api_url = service["api_url"]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.switching_service', service=service['name'])}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 切换到 {service['name']} 服务{Style.RESET_ALL}")
return self.create_email() # Recursively call
raise Exception(f"{self.translator.get('email.no_available_domains_after_filtering') if self.translator else '过滤后没有可用域名'}")
except Exception as e:
print(f"{Fore.RED}❌ 过滤域名时出错: {str(e)}{Style.RESET_ALL}")
raise
# Generate random username and password
try:
username, password = self._generate_credentials() username, password = self._generate_credentials()
self.password = password self.password = password
# 创建邮箱账户 # Create email account
email = f"{username}@{domains[0]['domain']}" selected_domain = filtered_domains[0]['domain']
email = f"{username}@{selected_domain}"
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.trying_to_create_email', email=email)}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 尝试创建邮箱: {email}{Style.RESET_ALL}")
account_data = { account_data = {
"address": email, "address": email,
"password": password "password": password
} }
except Exception as e:
print(f"{Fore.RED}❌ 生成凭据时出错: {str(e)}{Style.RESET_ALL}")
raise
# Create account
try:
create_response = requests.post(f"{self.api_url}/accounts", json=account_data, timeout=15)
create_response = requests.post(f"{self.api_url}/accounts", json=account_data)
if create_response.status_code != 201: if create_response.status_code != 201:
raise Exception(f"{self.translator.get('email.failed_to_create_account')}") if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_create_account', error=create_response.status_code)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建账户失败: 状态码 {create_response.status_code}{Style.RESET_ALL}")
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_create_account', error=create_response.text)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 响应内容: {create_response.text}{Style.RESET_ALL}")
# 获取访问令牌 # If it's a domain problem, try the next available domain
if len(filtered_domains) > 1 and ("domain" in create_response.text.lower() or "address" in create_response.text.lower()):
print(f"{Fore.YELLOW}⚠️ 尝试使用下一个可用域名...{Style.RESET_ALL}")
# Add current domain to blocked list
if selected_domain not in self.blocked_domains:
self.blocked_domains.append(selected_domain)
# Recursively call yourself
return self.create_email()
raise Exception(f"{self.translator.get('email.failed_to_create_account') if self.translator else '创建账户失败'}")
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_create_account', error=str(e))}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建账户时出错: {str(e)}{Style.RESET_ALL}")
raise
# Get access token
try:
token_data = { token_data = {
"address": email, "address": email,
"password": password "password": password
} }
token_response = requests.post(f"{self.api_url}/token", json=token_data) token_response = requests.post(f"{self.api_url}/token", json=token_data, timeout=10)
if token_response.status_code != 200: if token_response.status_code != 200:
raise Exception(f"{self.translator.get('email.failed_to_get_access_token')}") if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_get_access_token', error=token_response.status_code)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 获取令牌失败: 状态码 {token_response.status_code}{Style.RESET_ALL}")
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.failed_to_get_access_token', error=token_response.text)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 响应内容: {token_response.text}{Style.RESET_ALL}")
raise Exception(f"{self.translator.get('email.failed_to_get_access_token') if self.translator else '获取访问令牌失败'}")
self.token = token_response.json()["token"] self.token = token_response.json()["token"]
self.email = email self.email = email
except Exception as e:
print(f"{Fore.RED}❌ 获取令牌时出错: {str(e)}{Style.RESET_ALL}")
raise
if self.translator: if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}") print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
@@ -82,6 +216,9 @@ class NewTempEmail:
return email return email
except Exception as e: except Exception as e:
if attempt < max_retries:
print(f"{Fore.YELLOW}⚠️ 尝试重新创建邮箱... (尝试 {attempt}/{max_retries}){Style.RESET_ALL}")
else:
if self.translator: if self.translator:
print(f"{Fore.RED}{self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}") print(f"{Fore.RED}{self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}")
else: else:
@@ -89,19 +226,19 @@ class NewTempEmail:
return None return None
def close(self): def close(self):
"""关闭浏览器""" """close browser"""
if self.page: if self.page:
self.page.quit() self.page.quit()
def refresh_inbox(self): def refresh_inbox(self):
"""刷新邮箱""" """refresh inbox"""
try: try:
if self.translator: if self.translator:
print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}") print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}")
else: else:
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}") print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
# 使用 API 获取最新邮件 # Use API to get latest email
headers = {"Authorization": f"Bearer {self.token}"} headers = {"Authorization": f"Bearer {self.token}"}
response = requests.get(f"{self.api_url}/messages", headers=headers) response = requests.get(f"{self.api_url}/messages", headers=headers)
@@ -126,9 +263,9 @@ 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:
# 使用 API 获取邮件列表 # Use API to get email list
headers = {"Authorization": f"Bearer {self.token}"} headers = {"Authorization": f"Bearer {self.token}"}
response = requests.get(f"{self.api_url}/messages", headers=headers) response = requests.get(f"{self.api_url}/messages", headers=headers)
@@ -136,7 +273,7 @@ class NewTempEmail:
messages = response.json()["hydra:member"] messages = response.json()["hydra:member"]
for message in messages: for message in messages:
if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]: if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
# 获取邮件内容 # Get email content
message_id = message["id"] message_id = message["id"]
message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers) message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
if message_response.status_code == 200: if message_response.status_code == 200:
@@ -160,9 +297,9 @@ class NewTempEmail:
return False return False
def get_verification_code(self): def get_verification_code(self):
"""获取验证码""" """get verification code"""
try: try:
# 使用 API 获取邮件列表 # Use API to get email list
headers = {"Authorization": f"Bearer {self.token}"} headers = {"Authorization": f"Bearer {self.token}"}
response = requests.get(f"{self.api_url}/messages", headers=headers) response = requests.get(f"{self.api_url}/messages", headers=headers)
@@ -170,14 +307,14 @@ class NewTempEmail:
messages = response.json()["hydra:member"] messages = response.json()["hydra:member"]
for message in messages: for message in messages:
if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]: if message["from"]["address"] == "no-reply@cursor.sh" and "Verify your email address" in message["subject"]:
# 获取邮件内容 # Get email content
message_id = message["id"] message_id = message["id"]
message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers) message_response = requests.get(f"{self.api_url}/messages/{message_id}", headers=headers)
if message_response.status_code == 200: if message_response.status_code == 200:
# 从邮件内容中提取验证码 # Extract verification code from email content
email_content = message_response.json()["text"] email_content = message_response.json()["text"]
# 查找6位数字验证码 # Find 6-digit verification code
import re import re
code_match = re.search(r'\b\d{6}\b', email_content) code_match = re.search(r'\b\d{6}\b', email_content)
@@ -213,7 +350,7 @@ def main(translator=None):
else: else:
print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}")
# 测试刷新功能 # Test refresh function
while True: while True:
if translator: if translator:
choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower() choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower()

View File

@@ -10,6 +10,10 @@ import re
import tempfile import tempfile
from colorama import Fore, Style, init from colorama import Fore, Style, init
from typing import Tuple from typing import Tuple
import configparser
from new_signup import get_user_documents_path
import traceback
from config import get_config
# Initialize colorama # Initialize colorama
init() init()
@@ -28,24 +32,104 @@ 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_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
if not os.path.exists(config_file):
raise OSError(translator.get('reset.config_not_found') if translator else "找不到配置文件")
config.read(config_file, encoding='utf-8') # Specify encoding
# Get path based on system
if system == "Darwin":
section = 'MacPaths'
elif system == "Windows":
section = 'WindowsPaths'
elif system == "Linux":
section = 'LinuxPaths'
else:
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
if not config.has_section(section) or not config.has_option(section, 'cursor_path'):
raise OSError(translator.get('reset.path_not_configured') if translator else "未配置 Cursor 路徑")
base_path = config.get(section, 'cursor_path')
if not os.path.exists(base_path):
raise OSError(translator.get('reset.path_not_found', path=base_path) if translator else f"找不到 Cursor 路徑: {base_path}")
pkg_path = os.path.join(base_path, "package.json")
main_path = os.path.join(base_path, "out/main.js")
# Check if files exist
if not os.path.exists(pkg_path):
raise OSError(translator.get('reset.package_not_found', path=pkg_path) if translator else f"找不到 package.json: {pkg_path}")
if not os.path.exists(main_path):
raise OSError(translator.get('reset.main_not_found', path=main_path) if translator else f"找不到 main.js: {main_path}")
return (pkg_path, main_path)
def get_cursor_machine_id_path(translator=None) -> str:
"""
Get Cursor machineId file path based on operating system
Returns:
str: Path to machineId file
"""
# Read configuration
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
config = configparser.ConfigParser()
if os.path.exists(config_file):
config.read(config_file)
if sys.platform == "win32": # Windows
if not config.has_section('WindowsPaths'):
config.add_section('WindowsPaths')
config.set('WindowsPaths', 'machine_id_path',
os.path.join(os.getenv("APPDATA"), "Cursor", "machineId"))
return config.get('WindowsPaths', 'machine_id_path')
elif sys.platform == "linux": # Linux
if not config.has_section('LinuxPaths'):
config.add_section('LinuxPaths')
config.set('LinuxPaths', 'machine_id_path',
os.path.expanduser("~/.config/Cursor/machineId"))
return config.get('LinuxPaths', 'machine_id_path')
elif sys.platform == "darwin": # macOS
if not config.has_section('MacPaths'):
config.add_section('MacPaths')
config.set('MacPaths', 'machine_id_path',
os.path.expanduser("~/Library/Application Support/Cursor/machineId"))
return config.get('MacPaths', 'machine_id_path')
else:
raise OSError(f"Unsupported operating system: {sys.platform}")
# Save any changes to config file
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
def get_workbench_cursor_path(translator=None) -> str:
"""Get Cursor workbench.desktop.main.js path"""
system = platform.system()
paths_map = { paths_map = {
"Darwin": { "Darwin": { # macOS
"base": "/Applications/Cursor.app/Contents/Resources/app", "base": "/Applications/Cursor.app/Contents/Resources/app",
"package": "package.json", "main": "out/vs/workbench/workbench.desktop.main.js"
"main": "out/main.js",
}, },
"Windows": { "Windows": {
"base": os.path.join( "base": os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app"),
os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app" "main": "out/vs/workbench/workbench.desktop.main.js"
),
"package": "package.json",
"main": "out/main.js",
}, },
"Linux": { "Linux": {
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"], "bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app"],
"package": "package.json", "main": "out/vs/workbench/workbench.desktop.main.js"
"main": "out/main.js", }
},
} }
if system not in paths_map: if system not in paths_map:
@@ -53,16 +137,18 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
if system == "Linux": if system == "Linux":
for base in paths_map["Linux"]["bases"]: for base in paths_map["Linux"]["bases"]:
pkg_path = os.path.join(base, paths_map["Linux"]["package"]) main_path = os.path.join(base, paths_map["Linux"]["main"])
if os.path.exists(pkg_path): if os.path.exists(main_path):
return (pkg_path, os.path.join(base, paths_map["Linux"]["main"])) return main_path
raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径") raise OSError(translator.get('reset.linux_path_not_found') if translator else "在 Linux 系统上未找到 Cursor 安装路径")
base_path = paths_map[system]["base"] base_path = paths_map[system]["base"]
return ( main_path = os.path.join(base_path, paths_map[system]["main"])
os.path.join(base_path, paths_map[system]["package"]),
os.path.join(base_path, paths_map[system]["main"]), if not os.path.exists(main_path):
) raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
return main_path
def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool: def version_check(version: str, min_version: str = "", max_version: str = "", translator=None) -> bool:
"""Version number check""" """Version number check"""
@@ -95,11 +181,131 @@ def check_cursor_version(translator) -> bool:
"""Check Cursor version""" """Check Cursor version"""
try: try:
pkg_path, _ = get_cursor_paths(translator) pkg_path, _ = get_cursor_paths(translator)
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.reading_package_json', path=pkg_path)}{Style.RESET_ALL}")
try:
with open(pkg_path, "r", encoding="utf-8") as f: with open(pkg_path, "r", encoding="utf-8") as f:
version = json.load(f)["version"] data = json.load(f)
return version_check(version, min_version="0.45.0", translator=translator) except UnicodeDecodeError:
# If UTF-8 reading fails, try other encodings
with open(pkg_path, "r", encoding="latin-1") as f:
data = json.load(f)
if not isinstance(data, dict):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
return False
if "version" not in data:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.no_version_field')}{Style.RESET_ALL}")
return False
version = str(data["version"]).strip()
if not version:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_field_empty')}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.found_version', version=version)}{Style.RESET_ALL}")
# Check version format
if not re.match(r"^\d+\.\d+\.\d+$", version):
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_version_format', version=version)}{Style.RESET_ALL}")
return False
# Compare versions
try:
current = tuple(map(int, version.split(".")))
min_ver = (0, 45, 0) # Use tuple directly instead of string
if current >= min_ver:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.version_check_passed', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.version_too_low', version=version, min_version='0.45.0')}{Style.RESET_ALL}")
return False
except ValueError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.version_parse_error', error=str(e))}{Style.RESET_ALL}")
return False
except FileNotFoundError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.package_not_found', path=pkg_path)}{Style.RESET_ALL}")
return False
except json.JSONDecodeError as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.invalid_json_object')}{Style.RESET_ALL}")
return False
except Exception as e: except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.check_version_failed', error=str(e))}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('reset.stack_trace')}: {traceback.format_exc()}{Style.RESET_ALL}")
return False
def modify_workbench_js(file_path: str, translator=None) -> bool:
"""
Modify file content
"""
try:
# Save original file permissions
original_stat = os.stat(file_path)
original_mode = original_stat.st_mode
original_uid = original_stat.st_uid
original_gid = original_stat.st_gid
# Create temporary file
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", errors="ignore", delete=False) as tmp_file:
# Read original content
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
content = main_file.read()
if sys.platform == "win32":
# Define replacement patterns
CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "linux":
CButton_old_pattern = r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'$(k,E(Ks,{title:"yeongpin GitHub",size:"small",get codicon(){return F.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
elif sys.platform == "darwin":
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
CBadge_old_pattern = r'<div>Pro Trial'
CBadge_new_pattern = r'<div>Pro'
CToast_old_pattern = r'notifications-toasts'
CToast_new_pattern = r'notifications-toasts hidden'
# Replace content
content = content.replace(CButton_old_pattern, CButton_new_pattern)
content = content.replace(CBadge_old_pattern, CBadge_new_pattern)
content = content.replace(CToast_old_pattern, CToast_new_pattern)
# Write to temporary file
tmp_file.write(content)
tmp_path = tmp_file.name
# Backup original file
backup_path = file_path + ".backup"
if os.path.exists(backup_path):
os.remove(backup_path)
shutil.copy2(file_path, backup_path)
# Move temporary file to original position
if os.path.exists(file_path):
os.remove(file_path)
shutil.move(tmp_path, file_path)
# Restore original permissions
os.chmod(file_path, original_mode)
if os.name != "nt": # Not Windows
os.chown(file_path, original_uid, original_gid)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
if "tmp_path" in locals():
try:
os.unlink(tmp_path)
except:
pass
return False return False
def modify_main_js(main_path: str, translator) -> bool: def modify_main_js(main_path: str, translator) -> bool:
@@ -195,34 +401,73 @@ class MachineIDResetter:
def __init__(self, translator=None): def __init__(self, translator=None):
self.translator = translator self.translator = translator
# Read configuration
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
config = configparser.ConfigParser()
if not os.path.exists(config_file):
raise FileNotFoundError(f"Config file not found: {config_file}")
config.read(config_file, encoding='utf-8')
# Check operating system # Check operating system
if sys.platform == "win32": # Windows if sys.platform == "win32": # Windows
appdata = os.getenv("APPDATA") appdata = os.getenv("APPDATA")
if appdata is None: if appdata is None:
raise EnvironmentError("APPDATA Environment Variable Not Set") raise EnvironmentError("APPDATA Environment Variable Not Set")
self.db_path = os.path.join(
if not config.has_section('WindowsPaths'):
config.add_section('WindowsPaths')
config.set('WindowsPaths', 'storage_path', os.path.join(
appdata, "Cursor", "User", "globalStorage", "storage.json" appdata, "Cursor", "User", "globalStorage", "storage.json"
) ))
self.sqlite_path = os.path.join( config.set('WindowsPaths', 'sqlite_path', os.path.join(
appdata, "Cursor", "User", "globalStorage", "state.vscdb" appdata, "Cursor", "User", "globalStorage", "state.vscdb"
) ))
self.db_path = config.get('WindowsPaths', 'storage_path')
self.sqlite_path = config.get('WindowsPaths', 'sqlite_path')
elif sys.platform == "darwin": # macOS elif sys.platform == "darwin": # macOS
self.db_path = os.path.abspath(os.path.expanduser( if not config.has_section('MacPaths'):
config.add_section('MacPaths')
config.set('MacPaths', 'storage_path', os.path.abspath(os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/storage.json" "~/Library/Application Support/Cursor/User/globalStorage/storage.json"
)) )))
self.sqlite_path = os.path.abspath(os.path.expanduser( config.set('MacPaths', 'sqlite_path', os.path.abspath(os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb" "~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
)) )))
self.db_path = config.get('MacPaths', 'storage_path')
self.sqlite_path = config.get('MacPaths', 'sqlite_path')
elif sys.platform == "linux": # Linux elif sys.platform == "linux": # Linux
self.db_path = os.path.abspath(os.path.expanduser( if not config.has_section('LinuxPaths'):
"~/.config/Cursor/User/globalStorage/storage.json" config.add_section('LinuxPaths')
)) # Get actual user's home directory
self.sqlite_path = os.path.abspath(os.path.expanduser( sudo_user = os.environ.get('SUDO_USER')
"~/.config/Cursor/User/globalStorage/state.vscdb" actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
))
config.set('LinuxPaths', 'storage_path', os.path.abspath(os.path.join(
actual_home,
".config/Cursor/User/globalStorage/storage.json"
)))
config.set('LinuxPaths', 'sqlite_path', os.path.abspath(os.path.join(
actual_home,
".config/Cursor/User/globalStorage/state.vscdb"
)))
self.db_path = config.get('LinuxPaths', 'storage_path')
self.sqlite_path = config.get('LinuxPaths', 'sqlite_path')
else: else:
raise NotImplementedError(f"Not Supported OS: {sys.platform}") raise NotImplementedError(f"Not Supported OS: {sys.platform}")
# Save any changes to config file
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
def generate_new_ids(self): def generate_new_ids(self):
"""Generate new machine ID""" """Generate new machine ID"""
# Generate new UUID # Generate new UUID
@@ -237,6 +482,8 @@ class MachineIDResetter:
# Generate new sqmId # Generate new sqmId
sqm_id = "{" + str(uuid.uuid4()).upper() + "}" sqm_id = "{" + str(uuid.uuid4()).upper() + "}"
self.update_machine_id_file(dev_device_id)
return { return {
"telemetry.devDeviceId": dev_device_id, "telemetry.devDeviceId": dev_device_id,
"telemetry.macMachineId": mac_machine_id, "telemetry.macMachineId": mac_machine_id,
@@ -309,12 +556,12 @@ class MachineIDResetter:
new_guid = str(uuid.uuid4()) new_guid = str(uuid.uuid4())
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid) winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
winreg.CloseKey(key) winreg.CloseKey(key)
print("Windows MachineGuid updated successfully") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_guid_updated')}{Style.RESET_ALL}")
except PermissionError: except PermissionError:
print("Permission denied: Run as administrator to update Windows MachineGuid") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
raise raise
except Exception as e: except Exception as e:
print(f"Failed to update Windows MachineGuid: {e}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
raise raise
def _update_macos_platform_uuid(self, new_ids): def _update_macos_platform_uuid(self, new_ids):
@@ -326,11 +573,11 @@ class MachineIDResetter:
cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"' cmd = f'sudo plutil -replace "UUID" -string "{new_ids["telemetry.macMachineId"]}" "{uuid_file}"'
result = os.system(cmd) result = os.system(cmd)
if result == 0: if result == 0:
print("macOS Platform UUID updated successfully") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.macos_platform_uuid_updated')}{Style.RESET_ALL}")
else: else:
raise Exception("Failed to execute plutil command") raise Exception(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.failed_to_execute_plutil_command')}{Style.RESET_ALL}")
except Exception as e: except Exception as e:
print(f"Failed to update macOS Platform UUID: {e}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_macos_platform_uuid_failed', error=str(e))}{Style.RESET_ALL}")
raise raise
def reset_machine_ids(self): def reset_machine_ids(self):
@@ -373,7 +620,16 @@ class MachineIDResetter:
# Update system IDs # Update system IDs
self.update_system_ids(new_ids) self.update_system_ids(new_ids)
### Remove In v1.7.02
# Modify workbench.desktop.main.js
# workbench_path = get_workbench_cursor_path(self.translator)
# modify_workbench_js(workbench_path, self.translator)
### Remove In v1.7.02
# Check Cursor version and perform corresponding actions # Check Cursor version and perform corresponding actions
greater_than_0_45 = check_cursor_version(self.translator) greater_than_0_45 = check_cursor_version(self.translator)
if greater_than_0_45: if greater_than_0_45:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('reset.detecting_version')} >= 0.45.0{self.translator.get('reset.patching_getmachineid')}{Style.RESET_ALL}")
@@ -396,8 +652,48 @@ class MachineIDResetter:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.process_error', error=str(e))}{Style.RESET_ALL}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.process_error', error=str(e))}{Style.RESET_ALL}")
return False return False
def update_machine_id_file(self, machine_id: str) -> bool:
"""
Update machineId file with new machine_id
Args:
machine_id (str): New machine ID to write
Returns:
bool: True if successful, False otherwise
"""
try:
# Get the machineId file path
machine_id_path = get_cursor_machine_id_path()
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(machine_id_path), exist_ok=True)
# Create backup if file exists
if os.path.exists(machine_id_path):
backup_path = machine_id_path + ".backup"
try:
shutil.copy2(machine_id_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('reset.backup_created', path=backup_path) if self.translator else f'Backup created at: {backup_path}'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_creation_failed', error=str(e)) if self.translator else f'Could not create backup: {str(e)}'}{Style.RESET_ALL}")
# Write new machine ID to file
with open(machine_id_path, "w", encoding="utf-8") as f:
f.write(machine_id)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.update_success') if self.translator else 'Successfully updated machineId file'}{Style.RESET_ALL}")
return True
except Exception as e:
error_msg = f"Failed to update machineId file: {str(e)}"
if self.translator:
error_msg = self.translator.get('reset.update_failed', error=str(e))
print(f"{Fore.RED}{EMOJI['ERROR']} {error_msg}{Style.RESET_ALL}")
return False
def run(translator=None): def run(translator=None):
"""Convenient function for directly calling the reset function""" config = get_config(translator)
if not config:
return False
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}") print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}") print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")

View File

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

View File

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

32
utils.py Normal file
View File

@@ -0,0 +1,32 @@
import os
import sys
import platform
def get_user_documents_path():
"""Get user documents path"""
if platform.system() == "Windows":
return os.path.expanduser("~\\Documents")
else:
return os.path.expanduser("~/Documents")
def get_default_chrome_path():
"""Get default Chrome path"""
if sys.platform == "win32":
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
elif sys.platform == "darwin":
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
else:
return "/usr/bin/google-chrome"
def get_linux_cursor_path():
"""Get Linux Cursor path"""
possible_paths = [
"/opt/Cursor/resources/app",
"/usr/share/cursor/resources/app",
"/opt/cursor-bin/resources/app",
"/usr/lib/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])