Compare commits

...

89 Commits

Author SHA1 Message Date
yeongpin
bdb7fa4ddf Update README.md to clarify tool's purpose and usage, remove outdated features, and add new product image. 2025-04-16 10:41:29 +08:00
yeongpin
51ac969f76 Update version to 1.10.01, add function to retrieve user Documents folder path, and enhance CHANGELOG with new project guidelines and fixes. 2025-04-16 10:34:53 +08:00
Pin Studios
da9d4a3648 Update version to 1.9.06 and remove deprecated cursor authentication and registration scripts 2025-04-16 10:29:12 +08:00
Pin Studios
33a497bf52 Merge pull request #648 from tking007/main
fix: disable UPX in build.spec to resolve GLIBC dependency error
2025-04-16 01:23:19 +08:00
Pin Studios
c392e287fe Merge pull request #652 from BronzonTech-Cloud/main
Update block_domain.txt
2025-04-16 01:19:10 +08:00
Pin Studios
ea4cfaa7ab Merge pull request #651 from canmi21/main
sync(aur): 1.9.05
2025-04-16 01:18:12 +08:00
BronzonTech-Cloud
24c1acc562 Update block_domain.txt to add and reorder disposable email domains for improved filtering 2025-04-15 12:49:17 +00:00
Canmi
43eea92f21 sync: 1.9.05 2025-04-15 20:30:55 +08:00
limu3
631f8be5e4 fix: 禁用 build.spec 中的 UPX,解决 [PYI-714723:ERROR] Failed to load Python shared library ...,修复 Bug #620 2025-04-15 19:03:07 +08:00
yeongpin
c8fa00589e Update button replacement logic in bypass_token_limit.py to link to the yeongpin GitHub page, refining patterns for Windows, Linux, and Mac environments. 2025-04-15 12:05:12 +08:00
yeongpin
a3dff87da9 Enhance GitHub Actions workflow by adding code checkout step, calculating SHA256 checksums for release files, and extracting release notes from CHANGELOG. The release notes now include checksums for better transparency. 2025-04-15 10:41:46 +08:00
yeongpin
861f258ce4 Update CHANGELOG.md to reflect new version 1.9.04, including multiple fixes for package issues, readme formatting, and exception handling. 2025-04-15 10:33:08 +08:00
Pin Studios
ab9202fe48 Merge pull request #631 from xbhel/main
Fix ChromiumOptions.arguments type error: list object has no attribute 'get'
2025-04-15 10:28:41 +08:00
Pin Studios
d60c46bac6 Merge pull request #630 from canmi21/main
update: aur release 1.9.04 & fix(linux): pkg name & fix(chrome): oauth error
2025-04-15 10:27:43 +08:00
Pin Studios
4757f9777a Merge branch 'main' of https://github.com/canmi21/cursor into pr/597 2025-04-15 10:24:08 +08:00
Pin Studios
caecbd4c8d fix(readme): format download links for browsers 2025-04-15 10:24:07 +08:00
Canmi
105b5d4517 fix: github ouath fail error 2025-04-15 00:00:14 +08:00
Canmi
f325690e32 add: more output 2025-04-14 23:48:25 +08:00
Canmi
3de2db74b2 fix: unexpected 2025-04-14 23:32:49 +08:00
Canmi
6153041607 fix: handle 2025-04-14 23:22:56 +08:00
Canmi
6eba95c055 fix: oauth logic, add more debug output 2025-04-14 23:05:10 +08:00
Canmi
21535104a6 fix: google-chrome-stable on archlinx(linux), killall, port, and debug 2025-04-14 23:04:43 +08:00
Canmi
b42b4b01b9 fix: fetch 2025-04-14 22:02:10 +08:00
Canmi
4b9c465dd5 fix: path 2025-04-14 21:49:54 +08:00
Canmi
b98059a476 fix: missspell 2025-04-14 21:41:16 +08:00
xbhel
56882f0663 Fix ChromiumOptions#arguments has no attribute 'get' 2025-04-14 21:16:26 +08:00
Canmi
0852472746 fix: conflict 2025-04-14 21:10:56 +08:00
Canmi
d867f5cfe9 update: SRCINFO 2025-04-14 21:02:50 +08:00
Canmi
e69e500e8d add: LICENSE install 2025-04-14 21:02:34 +08:00
Canmi
2f012b9dc5 fix: missing LICENSE 2025-04-14 21:01:47 +08:00
Canmi
d3c6bf227b sync: aur version 2025-04-14 21:00:58 +08:00
Canmi
f697b71755 Merge branch 'yeongpin:main' into main 2025-04-14 20:59:13 +08:00
yeongpin
5863891f4b Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-04-14 16:24:53 +08:00
yeongpin
7e0da4a0cb Update version to 1.9.05 and enhance CHANGELOG with new features, fixes, and Python version update in Docker container. 2025-04-14 16:24:50 +08:00
Pin Studios
1cdb543ea9 Merge pull request #613 from haroondilshad/fix/f-string-backslash-errors
fix:  An error occurred: f-string expression part cannot include a b…
2025-04-14 16:24:04 +08:00
Pin Studios
5dad4f35a6 Merge pull request #614 from wang93wei/main
refactor: 优化语言逻辑 + 升级 Linux arm64 Docker Python 版本至 3.10
2025-04-14 16:20:32 +08:00
Pin Studios
34a23a69a1 Update README.md 2025-04-14 15:24:40 +08:00
Pin Studios
3cca2e3b17 Update README.md 2025-04-14 15:01:06 +08:00
alanwang
ee287b91f2 ci: 更新 ARM64 Docker 容器中的 Python 版本至 3.10
将 ARM64 Docker 容器中的 Python 版本从 3.9 更新至 3.10,以使用最新的稳定版本并确保兼容性
2025-04-14 14:31:06 +08:00
Pin Studios
96704e9f38 Update README.md 2025-04-14 14:21:42 +08:00
haroondilshad
f541bc40b4 fix: An error occurred: f-string expression part cannot include a backslash (delete_cursor_google.py, line 243) 2025-04-14 08:20:55 +02:00
alanwang
a730b145a1 refactor: 使用match-case重构语言映射和菜单选择逻辑
将语言映射和菜单选择逻辑从if-elif重构为match-case,提高代码可读性和维护性
2025-04-14 14:18:58 +08:00
yeongpin
1e72e59985 Update version in .env file to 1.9.04 2025-04-14 13:49:02 +08:00
yeongpin
7d355f126c Update CHANGELOG for v1.9.04, adding Opera GX support and fixing various issues. Update config and OAuth handler to include paths for Opera GX. Enhance utils to retrieve default browser paths for Opera GX. 2025-04-14 13:08:32 +08:00
Pin Studios
abcc3a84fa Update README.md 2025-04-14 12:52:45 +08:00
Pin Studios
d8f1cbfc3b Merge pull request #611 from yeongpin/revert-609-main
Revert "Create generator-generic-ossf-slsa3-publish.yml"
2025-04-14 12:43:02 +08:00
Pin Studios
431550e4a9 Revert "Create generator-generic-ossf-slsa3-publish.yml" 2025-04-14 12:42:45 +08:00
Pin Studios
dde25cba02 Merge pull request #609 from Martyb166/main
Create generator-generic-ossf-slsa3-publish.yml
2025-04-14 12:41:06 +08:00
Pin Studios
0466421823 Update README.md 2025-04-14 12:38:52 +08:00
Pin Studios
aa4177d5ec Update README.md 2025-04-14 12:35:12 +08:00
Pin Studios
75b17bb515 Update README.md 2025-04-14 12:31:10 +08:00
Pin Studios
80a76b507a Update README.md 2025-04-14 12:28:00 +08:00
Marcin Tyburski
84b77e8b13 Create generator-generic-ossf-slsa3-publish.yml 2025-04-14 06:20:18 +02:00
Pin Studios
f471450e12 Update README.md
Unable to select next GitHub token from pool
2025-04-14 12:01:18 +08:00
Canmi
9f81f94957 rm: duplicate content 2025-04-14 11:15:31 +08:00
Pin Studios
b423779d04 Merge pull request #602 from canmi21/main
fix(readme): missing linux
2025-04-14 01:00:17 +08:00
Canmi
5203634f0a Merge branch 'yeongpin:main' into main 2025-04-14 00:03:58 +08:00
Canmi
1a73ec0a32 fix(readme): missing linux 2025-04-14 00:03:17 +08:00
Pin Studios
92263013f2 Merge pull request #597 from canmi21/main
update(readme):  add linux & add: aur
2025-04-13 20:57:50 +08:00
Pin Studios
3edb69e831 update(readme): change section title for Archlinux 2025-04-13 20:57:34 +08:00
Pin Studios
271fc818b1 refactor(Translator): enhance language detection and fallback logic
Reorganize language detection methods for Windows and Unix systems, simplifying the fallback mechanism to ensure English is returned when detection fails. Introduce a mapping for language codes based on keyboard layout and system locale, improving maintainability and clarity in the code. This update also removes redundant code and enhances the overall structure of the Translator class.
2025-04-13 20:54:10 +08:00
Pin Studios
61803031dc Merge pull request #551 from paulpham157/feat/locales
[WIP] Support more new languages
2025-04-13 20:50:29 +08:00
Canmi
9a56bc5f7e fix: title 2025-04-13 20:19:54 +08:00
Canmi
32fea2c82b update(readme): add aur 2025-04-13 20:17:10 +08:00
Canmi
45f10a6da8 add: pkg 2025-04-13 20:11:44 +08:00
Canmi
a4692d11dc update(readme): add linux 2025-04-13 19:37:53 +08:00
Pin Studios
43a58db339 Update README.md 2025-04-13 16:57:03 +08:00
Pin Studios
bc55000668 Update README.md 2025-04-13 15:01:39 +08:00
paulpham157
84358805fc feat(locales): add new translation keys and update existing ones
Add new translation keys for various languages including zh_cn, pt, bg, ru, zh_tw, tr, nl, es, fr, and de. These changes include new error messages, prompts, and additional UI text to improve user experience and localization. Also, reorder initialization code in main.py for better readability.
2025-04-13 10:55:40 +07:00
paulpham157
82e2625dfe refactor(Translator): simplify language detection logic
Consolidate language detection logic for Windows and Unix systems by using a default layout mapping and simplifying locale extraction. Fallback to English if detection fails. This improves maintainability and reduces redundancy in the code.
2025-04-13 10:54:26 +07:00
paulpham157
d5404e8f57 fix: main.py:132: DeprecationWarning: 'locale.getdefaultlocale' is deprecated and slated for removal in Python 3.15. Use setlocale(), getencoding() and getlocale() instead. 2025-04-13 10:54:26 +07:00
yeongpin
fb3e532058 Update version to 1.9.03 and enhance CHANGELOG
- Updated the version in the .env file to 1.9.03.
- Added new entries in CHANGELOG.md for version 1.9.03, detailing hotfixes and improvements, including bypassing Cursor JWT expiration issues and fixing automatic logout in the Cursor editor.
- Refactored token extraction logic in cursor registration files to utilize a new method for improved reliability.
2025-04-13 02:11:59 +08:00
yeongpin
a7c4631ea4 Update CHANGELOG.md for version 1.9.02 with new features and fixes
- Added support for more browsers including Opera, Brave, Edge, and Firefox.
- Introduced a manual browser path configuration option.
- Enhanced existing features by fixing browser profile selection and addressing the Cursor JWT expiration issue.
- Retained fixes for configuration file path, Windows user permissions, and other minor issues.
2025-04-12 17:36:56 +08:00
yeongpin
5b64e54e90 Update CHANGELOG.md for version 1.9.02 with new features and fixes
- Added entry for bypassing Cursor JWT expiration issue.
- Fixed redirect issue in Cursor editor that caused automatic logout.
- Retained existing fixes for configuration file path, Windows user permissions, and other issues.
2025-04-12 17:31:14 +08:00
yeongpin
f667da64b3 Add token refresh functionality and improve token extraction
- Introduced a new `get_user_token.py` file to handle token refresh logic using the Chinese server API.
- Updated `config.py` to include new token settings for refresh server URL and enable refresh option.
- Refactored `oauth_auth.py` to utilize the new token extraction method, enhancing error handling and user feedback.
- Added localization strings for token refresh messages in both English and Chinese to improve user experience.
2025-04-12 17:28:11 +08:00
yeongpin
26a8e8da28 Update CHANGELOG.md for version 1.9.02 and enhance config handling
- Added entries for version 1.9.02 in CHANGELOG.md, detailing fixes for configuration file path, Windows user permissions, and other issues.
- Improved `config.py` to handle document path retrieval more robustly, including fallback to a temporary directory if the documents path is not found.
- Updated localization files to include new strings for configuration messages in both English and Chinese.
2025-04-12 17:00:18 +08:00
yeongpin
3862176867 Refactor README.md for improved readability
- Adjusted formatting of browser download instructions to enhance clarity and presentation.
- Separated links for better visual structure in both English and Chinese sections.
2025-04-12 14:37:32 +08:00
yeongpin
5adc598661 Update README.md and configuration files for enhanced browser support
- Revised browser download instructions in README.md to include multiple options: Google Chrome, Opera, Edge, Firefox, and Brave.
- Added new Windows path configurations for browser executables and drivers in the configuration section, improving support for various browsers and their respective drivers.
- Introduced new settings for OAuth configuration, including alert display options and timeout settings.
2025-04-12 14:36:37 +08:00
yeongpin
5e6651bb32 Update CHANGELOG.md for version 1.9.01
- Reorganized entries for clarity, ensuring the new features and fixes are listed under the correct version.
- Added details on enhanced browser support, including Opera, Brave, Edge, and Firefox, along with fixes for browser profile selection and other issues.
2025-04-12 14:35:10 +08:00
yeongpin
bdc606ce2d Update default version in build workflow to 1.9.01 2025-04-12 14:29:54 +08:00
yeongpin
564e421288 Update version to 1.9.01 and enhance CHANGELOG
- Updated the version in the .env file to 1.9.01.
- Added new entries in CHANGELOG.md for version 1.9.01, detailing enhancements such as increased browser support for Opera, Brave, Edge, and Firefox, along with fixes for browser profile selection and other issues.
2025-04-12 14:28:56 +08:00
yeongpin
dce359dc33 Enhance OAuth and browser configuration
- Updated `config.py` to include paths for Opera browser and added new OAuth settings for alert display and timeout configurations.
- Modified `oauth_auth.py` to improve error handling and user data directory retrieval for Opera, along with conditional alert display based on configuration.
- Enhanced `utils.py` to support multiple Opera installation paths for better browser detection.
- Updated localization files to include new strings for user data directory messages.
2025-04-12 14:26:46 +08:00
yeongpin
ff79fae77b Merge branch 'main' of https://github.com/yeongpin/cursor-free-vip 2025-04-12 14:11:24 +08:00
yeongpin
db3a2032dc Enhance browser configuration and path retrieval
- Updated `config.py` to include a comprehensive browser configuration section, allowing for dynamic retrieval of browser paths and drivers for Chrome, Edge, Firefox, and Brave.
- Refactored `new_signup.py`, `new_tempemail.py`, and `oauth_auth.py` to utilize the new browser configuration, improving flexibility and maintainability.
- Removed deprecated `get_default_chrome_path` function and replaced it with a more generalized `get_default_browser_path` function in `utils.py`.
- Updated localization files to include new strings related to browser path validation and selection.
2025-04-12 14:11:22 +08:00
Pin Studios
e2a33d178d Merge pull request #562 from eltociear/patch-1
docs: update CHANGELOG.md
2025-04-12 13:24:15 +08:00
Ikko Eltociear Ashimine
42d97cfa87 docs: update CHANGELOG.md
Faild -> Failed
2025-04-11 18:23:14 +09:00
yeongpin
c42d7d5422 Add option to bypass token limit in menu
- Updated the menu in `main.py` to include a new option for bypassing token limits.
- Adjusted the choice number in the main loop to accommodate the new functionality.
- Integrated the `bypass_token_limit` script to be executed when the new menu option is selected.
2025-04-11 10:54:29 +08:00
yeongpin
c7a84ca59f Update version to 1.8.11 in .env file 2025-04-11 10:53:49 +08:00
yeongpin
4746af7ce9 Add bypass token limit functionality
- Introduced a new script `bypass_token_limit.py` to modify the `workbench.desktop.main.js` file, allowing users to bypass token limits.
- Updated localization files to include new strings for the bypass token limit feature in English, Simplified Chinese, and Traditional Chinese.
- Enhanced CHANGELOG.md to reflect the addition of the bypass token limit feature and related fixes.
2025-04-11 10:53:06 +08:00
46 changed files with 1650 additions and 4349 deletions

21
.SRCINFO Normal file
View File

@@ -0,0 +1,21 @@
pkgbase = cursor-free-vip-git
pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit
pkgver = 1.9.05
pkgrel = 1
url = https://github.com/yeongpin/cursor-free-vip
arch = x86_64
license = MIT
license = Attribution-NonCommercial-NoDerivatives 4.0 International
makedepends = git
makedepends = python
makedepends = pyinstaller
makedepends = uv
depends = python
depends = cursor-bin
provides = cursor-free-vip
source = cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git
source = https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE
sha256sums = SKIP
sha256sums = SKIP
pkgname = cursor-free-vip-git

4
.env
View File

@@ -1,2 +1,2 @@
version=1.8.10
VERSION=1.8.10
version=1.10.01
VERSION=1.10.01

View File

@@ -6,7 +6,7 @@ on:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
default: '1.8.09'
default: '1.9.02'
permissions:
contents: write
@@ -153,7 +153,7 @@ jobs:
- 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 "
docker run --rm --platform linux/arm64 -v ${{ github.workspace }}:/app -w /app arm64v8/python:3.10-slim bash -c "
apt-get update && apt-get install -y build-essential
pip install --upgrade pip
pip install pyinstaller
@@ -211,6 +211,9 @@ jobs:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Get version
shell: bash
run: echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_ENV
@@ -220,18 +223,66 @@ jobs:
with:
path: artifacts
- name: Prepare release files
- name: Calculate SHA256 checksums
run: |
cd artifacts
echo "Contents of artifacts directory:"
ls -la
echo "Contents of subdirectories:"
ls -la */
mkdir -p checksums
for file in 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 }}_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
do
if [ -f "$file" ]; then
filename=$(basename $file)
sha256sum "$file" | cut -d ' ' -f 1 > checksums/${filename}.sha256
echo "${filename}: $(cat checksums/${filename}.sha256)" >> checksums/all_checksums.txt
else
echo "Warning: File $file not found"
fi
done
cat checksums/all_checksums.txt
- name: Extract release notes from CHANGELOG
run: |
version_pattern="## v${{ env.VERSION }}"
next_version_pattern="## v"
# Find the start line number of the current version
start_line=$(grep -n "$version_pattern" CHANGELOG.md | head -1 | cut -d: -f1)
if [ -z "$start_line" ]; then
echo "Error: Version ${{ env.VERSION }} not found in CHANGELOG.md"
exit 1
fi
# Find the line number of the next version
next_version_line=$(tail -n +$((start_line + 1)) CHANGELOG.md | grep -n "$next_version_pattern" | head -1 | cut -d: -f1)
if [ -z "$next_version_line" ]; then
# If there's no next version, get to the end of the file
changelog_content=$(tail -n +$start_line CHANGELOG.md)
else
# Extract content between current version and next version
end_line=$((start_line + next_version_line - 1))
changelog_content=$(sed -n "${start_line},${end_line}p" CHANGELOG.md)
fi
# Create release notes file
{
echo "$changelog_content"
echo ""
echo "## SHA256 Checksums"
cat checksums/all_checksums.txt
} > release_notes.md
# Display release notes for debugging
cat release_notes.md
- name: Create Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{ env.VERSION }}
body_path: release_notes.md
files: |
artifacts/CursorFreeVIP_${{ env.VERSION }}_windows.exe/CursorFreeVIP_${{ env.VERSION }}_windows.exe
artifacts/CursorFreeVIP_${{ env.VERSION }}_mac_arm64/CursorFreeVIP_${{ env.VERSION }}_mac_arm64

View File

@@ -1,5 +1,62 @@
# Change Log
## v1.10.01
1. Remove: Remove All Auto generating fake Google email accounts and OAuth access | 移除所有自動生成假 Google 電子郵件帳戶和 OAuth 訪問
2. Follow GitHub Terms of Service | 遵守 GitHub Terms of Service
3. Follow Cursor Terms of Service | 遵守 Cursor Terms of Service
4. All are for educational purposes, currently the repo does not violate any laws | 全都是教育用途,目前 repo 沒有違反任何法律
5. This project adopts CC BY-NC-ND 4.0 , do not use for commercial purposes | 本專案採用 CC BY-NC-ND 4.0,拒絕任何商業用途
6. Use & Cherish | 切用且珍惜
7. Fix: Some Issues | 修復一些問題
## v1.9.05
1. Refactor: Using match-case to refactor language mapping and menu selection logic, making the code clearer and more maintainable. | 使用 match-case 重构语言映射和菜单选择逻辑,使代码更清晰、可维护性更高。
2. Ci: Update the Python version in the ARM64 Docker build container to 3.10, making it more compatible and easier to migrate in the future. | 更新 ARM64 Docker 构建容器中的 Python 版本至 3.10,兼容性更强,方便未来迁移。
3. Fix: f-string backslash expression errors in multiple files | 修復多個文件中的 f-string 反斜杠表達式錯誤
4. Sync AUR new version 1.9.04 | 同步 AUR 新版本 1.9.04
5. Fix: missing license install on pkgbuild @michaeldavis246611119 mention here | 修復 pkgbuild 中缺少授權安裝 @michaeldavis246611119 提到這裡
6. Fix: readme table | 修復 readme 表格
7. Fix: google-chrome package name problem, add "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616 | 修復 google-chrome 包名稱問題,添加 "google-chrome-stable" [Bug]: Chrome error | Arch | gnome | AUR chrome #242 [Discussion]: how to use the new feature, Register with Google Account #249 [Discussion]: Having issues using the script in Ubuntu #487 [Bug]: Can open chromium bin in linux #616
8. Fix: exception error log | 修復異常錯誤日誌
9. Fix: github oauth error [Bug]: #564 | 修復 github oauth 錯誤 [Bug]: #564
10. Fix: ChromiumOptions.arguments type error: list object has no attribute 'get' | 修復 ChromiumOptions.arguments 類型錯誤list 對象沒有屬性 'get'
11. Fix: Some Issues | 修復一些問題
## v1.9.04
1. Add: Opera GX Support | 添加 Opera GX 支持
2. Same as v1.9.03 | 與 v1.9.03 相同
3. Hotfix: Some Issues | 修復一些問題
4. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
5. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
6. Fix: Some Issues | 修復一些問題
## v1.9.03[Skip & Merge to v1.9.04]
1. Hotfix: Some Issues | 修復一些問題
2. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
3. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
4. Fix: Some Issues | 修復一些問題
## v1.9.02
1. Add: Bypass Token Limit | 添加繞過 Token 限制
2. Add: More Browser Support | 添加更多瀏覽器支持
3. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
4. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
5. Add config manual browser path | 添加配置手動選擇遊覽器路徑
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
6. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
7. Fix: Config File Path | 修復配置文件路徑
8. Fix: window user permission | 修復 window 用戶權限
9. Fix: Some Issues | 修復一些問題
## v1.9.01
1. Add: Bypass Token Limit | 添加繞過 Token 限制
2. Add: More Browser Support | 添加更多瀏覽器支持
3. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
4. Add config manual browser path | 添加配置手動選擇遊覽器路徑
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
6. Fix: Some Issues | 修復一些問題
## v1.8.10
1. Add: Check User Authorized | 添加檢查用戶授權
2. Fix: Linux Reset Process Error: 'base' | 修復 Linux 重置過程錯誤:'base'
@@ -271,7 +328,7 @@ These changes make the application more user-friendly by only requesting admin p
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 失敗
4. Fix: Update Curl Failed | 修復更新 Curl 失敗
## v1.5.03
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題

View File

@@ -1,5 +0,0 @@
// Import the contents of your other files
importScripts('default_filters.js', 'block.js');
// Your service worker initialization code can go here
console.log('Service worker initialized');

View File

@@ -1,129 +0,0 @@
/**
* All the actual functionality of the extension; loads as part of the background page.
*
* Active ingredient is enable(), which sets up the webRequest callbacks.
*
* */
let blockingEnabled = false;
let allFilters = null;
let webRTCPrivacy = null;
function setFilters(newFilters) {
allFilters = newFilters;
chrome.storage.local.set({"filters": newFilters});
if (blockingEnabled) {
refreshFilters();
}
}
// Convert URL patterns to declarativeNetRequest rule format
function createRules(filters) {
return filters.map((filter, index) => ({
id: index + 1,
priority: 1,
action: {
type: "block"
},
condition: {
urlFilter: filter.replace("*://", "*"),
resourceTypes: [
"main_frame", "sub_frame", "stylesheet", "script", "image",
"font", "object", "xmlhttprequest", "ping", "media", "websocket"
]
}
}));
}
async function enable(icon = true) {
if (blockingEnabled) return;
if (allFilters && allFilters.length > 0) {
const rules = createRules(allFilters);
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: rules.map(rule => rule.id),
addRules: rules
});
}
blockingEnabled = true;
if (icon) {
chrome.action.setIcon({
path: {
"16": "enabled16.png",
"48": "enabled48.png"
}
});
}
}
async function disable(icon = true) {
if (!blockingEnabled) return;
const rules = await chrome.declarativeNetRequest.getDynamicRules();
await chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: rules.map(rule => rule.id),
addRules: []
});
blockingEnabled = false;
if (icon) {
chrome.action.setIcon({
path: {
"16": "disabled.png",
"32": "disabled.png"
}
});
}
}
async function refreshFilters() {
await disable(false);
await enable(true);
}
async function toggleEnabled() {
if (blockingEnabled) {
await disable();
} else {
await enable();
}
}
function setWebRTCPrivacy(flag, store = true) {
webRTCPrivacy = flag;
const privacySetting = flag ? "default_public_interface_only" : "default";
chrome.privacy.network.webRTCIPHandlingPolicy.set({value: privacySetting});
if (store) {
chrome.storage.local.set({"webrtc_privacy": flag});
}
}
// Initialization
chrome.storage.local.get("filters",
function(result) {
if (result["filters"] == undefined) {
console.log("Initializing filters to defaults.");
setFilters(defaultFilters);
} else {
setFilters(result["filters"]);
allFilters = result["filters"];
}
// toggle blocking on-off via the extension icon
chrome.action.onClicked.addListener(toggleEnabled);
// initialize blocking
enable();
}
);
chrome.storage.local.get("webrtc_privacy",
function(result) {
if (result["webrtc_privacy"] == undefined) {
console.log("Initializing WebRTC privacy to default.");
setWebRTCPrivacy(false, true);
} else {
setWebRTCPrivacy(result["webrtc_privacy"], false);
}
}
);

View File

@@ -1,14 +0,0 @@
defaultFilters = [
// personally, I can't stand the like box
//"http://www.facebook.com/plugins/likebox.php?*",
"*://*.doubleclick.net/*",
"*://partner.googleadservices.com/*",
"*://*.googlesyndication.com/*",
"*://*.google-analytics.com/*",
"*://creative.ak.fbcdn.net/*",
"*://*.adbrite.com/*",
"*://*.exponential.com/*",
"*://*.quantserve.com/*",
"*://*.scorecardresearch.com/*",
"*://*.zedo.com/*",
]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 850 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1,29 +0,0 @@
{
"name": "PBlock",
"version": "0.1",
"manifest_version": 3,
"author": "yeongpin",
"url": "https://github.com/yeongpin/PBlock",
"description": "Just a simple blocker.",
"action": {
"default_icon": "enabled48.png"
},
"background": {
"service_worker": "background.js"
},
"icons": {
"16": "enabled16.png",
"48": "enabled48.png"
},
"permissions": [
"declarativeNetRequest",
"storage",
"privacy"
],
"host_permissions": [
"http://*/*",
"https://*/*",
"ws://*/*",
"wss://*/*"
]
}

View File

@@ -1,5 +0,0 @@
{
"id": "ruleset_1",
"version": "1.0",
"rules": []
}

34
PKGBUILD Normal file
View File

@@ -0,0 +1,34 @@
# Maintainer: Canmi21 <9997200@qq.com>
# Contributor: Canmi (Canmi21)
pkgname=cursor-free-vip-git
pkgver=1.9.05
pkgrel=1
pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit"
arch=('x86_64')
url="https://github.com/yeongpin/cursor-free-vip"
license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International')
depends=('python' 'cursor-bin')
makedepends=('git' 'python' 'pyinstaller' 'uv')
provides=('cursor-free-vip')
source=("cursor-free-vip::git+https://github.com/yeongpin/cursor-free-vip.git" "https://raw.githubusercontent.com/canmi21/openjlc/refs/heads/main/LICENSE")
sha256sums=('SKIP' 'SKIP')
pkgver() {
cd "$srcdir/cursor-free-vip"
git describe --tags --always | sed 's/^v//;s/-/./g'
}
build() {
cd "$srcdir/cursor-free-vip"
uv venv .venv
source .venv/bin/activate
uv pip install -r requirements.txt
pyinstaller --clean --noconfirm --onefile main.py --name cursor-free-vip
}
package() {
install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license"
install -Dm644 "$srcdir/cursor-free-vip/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/attribution_non_commercial_no_derivatives_license"
install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip"
}

View File

@@ -7,33 +7,42 @@
<p align="center">
[![Release](https://img.shields.io/github/v/release/yeongpin/cursor-free-vip?style=flat-square&logo=github&color=blue)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
[![Release](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/release/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
[![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC_BY--NC--ND_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-nd/4.0/)
[![Stars](https://img.shields.io/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers)
[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a1)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
[![Stars](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/stars/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/stargazers)
[![Downloads](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/downloads/yeongpin/cursor-free-vip/total)](https://github.com/yeongpin/cursor-free-vip/releases/latest)
<a href="https://buymeacoffee.com/yeongpin" target="_blank"><img alt="Buy Me a Coffee" src="https://img.shields.io/badge/Buy%20Me%20a%20Coffee-Support%20Me-FFDA33"></a>
</p>
<a href="https://trendshift.io/repositories/13425" target="_blank"><img src="https://trendshift.io/api/badge/repositories/13425" alt="yeongpin%2Fcursor-free-vip | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<br>
<a href="https://www.buymeacoffee.com/yeongpin" target="_blank">
<img src="https://img.buymeacoffee.com/button-api/?text=buy me a coffee&emoji=☕&slug=yeongpin&button_colour=ffda33&font_colour=000000&font_family=Bree&outline_colour=000000&coffee_colour=FFDD00&latest=2" width="160" height='55' alt="Buy Me a Coffee"/>
</a>
<h4>Support Latest 0.48.x Version | 支持最新 0.48.x 版本</h4>
This tool registers accounts with custom emails, support Google and GitHub account registrations, temporary GitHub account registration, kills all Cursor's running processes, resets and wipes Cursor data and hardware info.
This tool is for educational purposes, currently the repo does not violate any laws. Please support the original project.
This tool will not generate any fake email accounts and OAuth access.
Supports Windows, macOS and Linux.
For optimal performance, run with privileges and always stay up to date.
Always clean your browser's cache and cookies. If possible, use a VPN to create new accounts.
這是一款用於學習和研究的工具,目前 repo 沒有違反任何法律。請支持原作者。
這款工具不會生成任何假的電子郵件帳戶和 OAuth 訪問。
支持 Windows、macOS 和 Linux。
對於最佳性能,請以管理員身份運行並始終保持最新。
這是一個自動化工具,自動註冊,支持 Windows 和 macOS 系統,完成 Auth 驗證,重置 Cursor 的配置。
<p align="center">
<img src="./images/pro_2025-04-05_18-47-56.png" alt="new" width="800" style="border-radius: 6px;"/><br>
<img src="./images/product_2025-04-16_10-40-21.png" alt="new" width="800" style="border-radius: 6px;"/><br>
</p>
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
##### 如果沒有 Google Chrome可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
</div>
## 🔄 Change Log | 更新日志
@@ -42,42 +51,40 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
## ✨ Features | 功能特點
* 🌟 Google OAuth Authentication with Lifetime Access<br>使用 Google OAuth 認證(終身訪問)<br>
* ⭐ GitHub OAuth Authentication with Lifetime Access<br>使用 GitHub OAuth 認證(終身訪問)<br>
* Automatically register Cursor membership<br>自動註冊 Cursor 會員<br>
* Support Windows and macOS systems<br>支持 Windows 和 macOS 系統<br>
* Complete Auth verification<br>完成 Auth 驗證<br>
* Support Windows macOS and Linux systems<br>支持 Windows、macOS 和 Linux 系統<br>
* Reset Cursor's configuration<br>重置 Cursor 的配置<br>
* Delete Cursor Google Account<br>删除 Cursor Google 账号<br>
* Multi-language support (English, 简体中文, 繁體中文, Vietnamese)<br>多語言支持(英文、简体中文、繁體中文、越南語)<br>
## 💻 System Support | 系統支持
| Windows | x64 | ✅ | macOS | Intel | ✅ |
|:-------:|:-----:|:-:|:-----:|:-------------:|:-:|
| Windows | x86 | ✅ | macOS | Apple Silicon | ✅ |
| Linux | x64 | ✅ | Linux | x86 | ✅ |
| Linux | ARM64 | ✅ | Linux | ARM64 | ✅ |
| Operating System | Architecture | Supported |
|------------------|-------------------|-----------|
| Windows | x64, x86 | ✅ |
| macOS | Intel, Apple Silicon | ✅ |
| Linux | x64, x86, ARM64 | ✅ |
## 👀 How to use | 如何使用
<details open>
<summary><b>⭐ Auto Run Script | 腳本自動化運行</b></summary>
**Linux/macOS**
### **Linux/macOS**
```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.sh -o install.sh && chmod +x install.sh && ./install.sh
```
**Windows**
### **Archlinux**
Install via [AUR](https://aur.archlinux.org/packages/cursor-free-vip-git)
```bash
yay -S cursor-free-vip-git
```
### **Windows**
```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/install.ps1 | iex
@@ -88,13 +95,13 @@ irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/inst
<details>
<summary><b>⭐ Manual Reset Machine | 手動運行重置機器</b></summary>
**Linux/macOS**
### **Linux/macOS**
```bash
curl -fsSL https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.sh | sudo bash
```
**Windows**
### **Windows**
```powershell
irm https://raw.githubusercontent.com/yeongpin/cursor-free-vip/main/scripts/reset.ps1 | iex
@@ -166,6 +173,33 @@ max_timeout = 160
check_update = True
# Show Account Info | 顯示賬號信息
show_account_info = True
[WindowsPaths]
storage_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\storage.json
sqlite_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\state.vscdb
machine_id_path = C:\Users\yeongpin\AppData\Roaming\Cursor\machineId
cursor_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app
updater_path = C:\Users\yeongpin\AppData\Local\cursor-updater
update_yml_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app-update.yml
product_json_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app\product.json
[Browser]
default_browser = opera
chrome_path = C:\Program Files\Google\Chrome\Application\chrome.exe
edge_path = C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
firefox_path = C:\Program Files\Mozilla Firefox\firefox.exe
brave_path = C:\Program Files\BraveSoftware/Brave-Browser/Application/brave.exe
chrome_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
edge_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\msedgedriver.exe
firefox_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\geckodriver.exe
brave_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
opera_path = C:\Users\yeongpin\AppData\Local\Programs\Opera\opera.exe
opera_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
[OAuth]
show_selection_alert = False
timeout = 120
max_attempts = 3
```
</details>

View File

@@ -1,83 +1,134 @@
oakon.com
famamail.com
2925.com
indigobook.com
teihu.com
raleigh-construction.com
pastryofistanbul.com
linshiyouxiang.net
Mohmal.com
pusmail.com
questtechsystems.com
ikomail.com
ofanda.com
pusmail.com
ikomail.com
mailpull.com
drewzen.com
begemail.com
dugmail.com
solerbe.net
corhash.net
mailshou.com
0-mail.com
10minemail.com
1secmail.com
20minutemail.com
2925.com
2prong.com
33mail.com
abusemail.de
afrobacon.com
anonbox.net
anonymbox.com
antichef.com
bareed.ws
begemail.com
boun.cr
brefmail.com
burnermail.io
byom.de
chammy.info
cloud-mail.top
cocovpn.com
cool.fr.nf
corhash.net
crazymailing.com
cuvox.de
dayrep.com
deadaddress.com
discard.email
dispostable.com
drewzen.com
dudmail.com
dugmail.com
emailondeck.com
emailtemporario.com.br
ephemail.net
fakeinbox.com
fakeinbox.org
fakemailgenerator.com
famamail.com
fastmailbox.net
filzmail.com
fizmail.com
getairmail.com
getnada.com
givmail.com
guerrillamail.com
gustr.com
harakirimail.com
hottempmail.com
ikomail.com
inboxbear.com
inboxkitten.com
incognitomail.org
indigobook.com
jetable.org
kaspop.com
letthemeatspam.com
linshiyouxiang.net
luxusmail.org
mail-temp.com
mail1a.de
mailbucket.org
mailcatch.com
maildrop.cc
mailexpire.com
mailhazard.com
mailimate.com
mailin8r.com
mailinator.com
mailme.lv
mailnesia.com
mailnull.com
mailpull.com
mailsac.com
mailshou.com
mailtemp.net
mailzilla.org
meltmail.com
mintemail.com
moakt.com
mohmal.com
my10minutemail.com
mycleaninbox.net
mytrashmail.com
no-spam.ws
nomail.pw
nospamfor.us
notmailinator.com
nowmymail.com
oakon.com
objectmail.com
ofanda.com
openmailbox.org
owlpic.com
pastryofistanbul.com
privacyroot.com
pusmail.com
questtechsystems.com
raleigh-construction.com
rcpt.at
safemail.link
sendspamhere.com
sharklasers.com
shortmail.net
solerbe.net
spam4.me
spamavert.com
spambog.com
spamdecoy.net
spamex.com
spamfree24.org
spamgourmet.com
spamhereplease.com
spaml.com
spamslicer.com
spamsphere.com
spamtroll.net
teihu.com
temp-mail.org
tempmail.net
tempmailaddress.com
temporaryemail.net
throwawayemail.com
tmail.ws
trash-mail.com
trash2009.com
trashdevil.com
trashmail.com
trashmail.de
trbvn.com
wegwerfadresse.org
yepmail.net
yopmail.com
zippymail.info
zippymail.info

View File

@@ -23,29 +23,14 @@ a = Analysis(
pathex=[],
binaries=[],
datas=[
('turnstilePatch', 'turnstilePatch'),
('PBlock', 'PBlock'),
('locales', 'locales'),
('cursor_auth.py', '.'),
('reset_machine_manual.py', '.'),
('cursor_register.py', '.'),
('new_signup.py', '.'),
('new_tempemail.py', '.'),
('quit_cursor.py', '.'),
('cursor_register_manual.py', '.'),
('oauth_auth.py', '.'),
('utils.py', '.'),
('.env', '.'),
('block_domain.txt', '.')
],
hiddenimports=[
'cursor_auth',
'reset_machine_manual',
'new_signup',
'new_tempemail',
'quit_cursor',
'cursor_register_manual',
'oauth_auth',
'utils'
],
hookspath=[],
@@ -69,7 +54,7 @@ exe = EXE(
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx=False,
upx_exclude=[],
runtime_tmpdir=None,
console=True,

190
bypass_token_limit.py Normal file
View File

@@ -0,0 +1,190 @@
import os
import shutil
import platform
import tempfile
import glob
from colorama import Fore, Style, init
import configparser
import sys
from config import get_config
from datetime import datetime
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
"FILE": "📄",
"BACKUP": "💾",
"SUCCESS": "",
"ERROR": "",
"INFO": "",
"RESET": "🔄",
"WARNING": "⚠️",
}
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_workbench_cursor_path(translator=None) -> str:
"""Get Cursor workbench.desktop.main.js path"""
system = platform.system()
# 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)
paths_map = {
"Darwin": { # macOS
"base": "/Applications/Cursor.app/Contents/Resources/app",
"main": "out/vs/workbench/workbench.desktop.main.js"
},
"Windows": {
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
},
"Linux": {
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
"main": "out/vs/workbench/workbench.desktop.main.js"
}
}
if system == "Linux":
# Add extracted AppImage with correct usr structure
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
if system not in paths_map:
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
if system == "Linux":
for base in paths_map["Linux"]["bases"]:
main_path = os.path.join(base, paths_map["Linux"]["main"])
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}")
if os.path.exists(main_path):
return main_path
if system == "Windows":
base_path = config.get('WindowsPaths', 'cursor_path')
elif system == "Darwin":
base_path = paths_map[system]["base"]
else: # Linux
# For Linux, we've already checked all bases in the loop above
# If we're here, it means none of the bases worked, so we'll use the first one
base_path = paths_map[system]["bases"][0]
main_path = os.path.join(base_path, paths_map[system]["main"])
if not os.path.exists(main_path):
raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
return main_path
def 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()
patterns = {
# 通用按钮替换模式
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
# Windows/Linux
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
# Mac 通用按钮替换模式
r'$(k,E(Ks,{title:"Upgrade to Pro",size:"small",get codicon(){return F.rocket},get onClick(){return t.pay}}),null)': 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)',
# Badge 替换
r'<div>Pro Trial': r'<div>Pro',
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
#
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
# Pro
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
# Toast 替换
r'notifications-toasts': r'notifications-toasts hidden'
}
# 使用patterns进行替换
for old_pattern, new_pattern in patterns.items():
content = content.replace(old_pattern, new_pattern)
# Write to temporary file
tmp_file.write(content)
tmp_path = tmp_file.name
# Backup original file with timestamp
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
backup_path = f"{file_path}.backup.{timestamp}"
shutil.copy2(file_path, backup_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
# Move temporary file to original position
if os.path.exists(file_path):
os.remove(file_path)
shutil.move(tmp_path, file_path)
# Restore original permissions
os.chmod(file_path, original_mode)
if os.name != "nt": # Not Windows
os.chown(file_path, original_uid, original_gid)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
if "tmp_path" in locals():
try:
os.unlink(tmp_path)
except:
pass
return False
def run(translator=None):
config = get_config(translator)
if not config:
return False
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('bypass_token_limit.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
modify_workbench_js(get_workbench_cursor_path(translator), translator)
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('bypass_token_limit.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
run(main_translator)

View File

@@ -2,7 +2,7 @@ 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
from utils import get_user_documents_path, get_linux_cursor_path, get_default_driver_path, get_default_browser_path
import shutil
import datetime
@@ -18,19 +18,59 @@ EMOJI = {
"SETTINGS": "⚙️"
}
# global config cache
_config_cache = None
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)
# get documents path
docs_path = get_user_documents_path()
if not docs_path or not os.path.exists(docs_path):
# if documents path not found, use current directory
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.documents_path_not_found', fallback='Documents path not found, using current directory') if translator else 'Documents path not found, using current directory'}{Style.RESET_ALL}")
docs_path = os.path.abspath('.')
# normalize path
config_dir = os.path.normpath(os.path.join(docs_path, ".cursor-free-vip"))
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
# create config directory, only print message when directory not exists
dir_exists = os.path.exists(config_dir)
try:
os.makedirs(config_dir, exist_ok=True)
if not dir_exists: # only print message when directory not exists
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_dir_created', path=config_dir) if translator else f'Config directory created: {config_dir}'}{Style.RESET_ALL}")
except Exception as e:
# if cannot create directory, use temporary directory
import tempfile
temp_dir = os.path.normpath(os.path.join(tempfile.gettempdir(), ".cursor-free-vip"))
temp_exists = os.path.exists(temp_dir)
config_dir = temp_dir
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
os.makedirs(config_dir, exist_ok=True)
if not temp_exists: # only print message when temporary directory not exists
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.using_temp_dir', path=config_dir, error=str(e)) if translator else f'Using temporary directory due to error: {config_dir} (Error: {str(e)})'}{Style.RESET_ALL}")
# create config object
config = configparser.ConfigParser()
# Default configuration
default_config = {
'Chrome': {
'chromepath': get_default_chrome_path()
'Browser': {
'default_browser': 'chrome',
'chrome_path': get_default_browser_path('chrome'),
'chrome_driver_path': get_default_driver_path('chrome'),
'edge_path': get_default_browser_path('edge'),
'edge_driver_path': get_default_driver_path('edge'),
'firefox_path': get_default_browser_path('firefox'),
'firefox_driver_path': get_default_driver_path('firefox'),
'brave_path': get_default_browser_path('brave'),
'brave_driver_path': get_default_driver_path('brave'),
'opera_path': get_default_browser_path('opera'),
'opera_driver_path': get_default_driver_path('opera'),
'operagx_path': get_default_browser_path('operagx'),
'operagx_driver_path': get_default_driver_path('chrome') # Opera GX 使用 Chrome 驱动
},
'Turnstile': {
'handle_turnstile_time': '2',
@@ -56,6 +96,15 @@ def setup_config(translator=None):
'enabled_update_check': 'True',
'enabled_force_update': 'False',
'enabled_account_info': 'True'
},
'OAuth': {
'show_selection_alert': False, # 默认不显示选择提示弹窗
'timeout': 120,
'max_attempts': 3
},
'Token': {
'refresh_server': 'https://token.cursorpro.com.cn',
'enable_refresh': True
}
}
@@ -312,4 +361,7 @@ def force_update_config(translator=None):
def get_config(translator=None):
"""Get existing config or create new one"""
return setup_config(translator)
global _config_cache
if _config_cache is None:
_config_cache = setup_config(translator)
return _config_cache

View File

@@ -1,159 +0,0 @@
import sqlite3
import os
import sys
from colorama import Fore, Style, init
from config import get_config
# Initialize colorama
init()
# Define emoji and color constants
EMOJI = {
'DB': '🗄️',
'UPDATE': '🔄',
'SUCCESS': '',
'ERROR': '',
'WARN': '⚠️',
'INFO': '',
'FILE': '📄',
'KEY': '🔐'
}
class CursorAuth:
def __init__(self, translator=None):
self.translator = translator
# Get configuration
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}")
sys.exit(1)
# Get path based on operating system
try:
if sys.platform == "win32": # Windows
if not config.has_section('WindowsPaths'):
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):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_not_found', path=self.db_path)}{Style.RESET_ALL}")
return
# Check file permissions
if not os.access(self.db_path, os.R_OK | os.W_OK):
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_permission_error')}{Style.RESET_ALL}")
return
try:
self.conn = sqlite3.connect(self.db_path)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
except sqlite3.Error as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.db_connection_error', error=str(e))}{Style.RESET_ALL}")
return
def update_auth(self, email=None, access_token=None, refresh_token=None):
conn = None
try:
# Ensure the directory exists and set the correct permissions
db_dir = os.path.dirname(self.db_path)
if not os.path.exists(db_dir):
os.makedirs(db_dir, mode=0o755, exist_ok=True)
# If the database file does not exist, create a new one
if not os.path.exists(self.db_path):
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
CREATE TABLE IF NOT EXISTS ItemTable (
key TEXT PRIMARY KEY,
value TEXT
)
''')
conn.commit()
if sys.platform != "win32":
os.chmod(self.db_path, 0o644)
conn.close()
# Reconnect to the database
conn = sqlite3.connect(self.db_path)
print(f"{EMOJI['INFO']} {Fore.GREEN} {self.translator.get('auth.connected_to_database')}{Style.RESET_ALL}")
cursor = conn.cursor()
# Add timeout and other optimization settings
conn.execute("PRAGMA busy_timeout = 5000")
conn.execute("PRAGMA journal_mode = WAL")
conn.execute("PRAGMA synchronous = NORMAL")
# Set the key-value pairs to update
updates = []
updates.append(("cursorAuth/cachedSignUpType", "Auth_0"))
if email is not None:
updates.append(("cursorAuth/cachedEmail", email))
if access_token is not None:
updates.append(("cursorAuth/accessToken", access_token))
if refresh_token is not None:
updates.append(("cursorAuth/refreshToken", refresh_token))
# Use transactions to ensure data integrity
cursor.execute("BEGIN TRANSACTION")
try:
for key, value in updates:
# Check if the key exists
cursor.execute("SELECT COUNT(*) FROM ItemTable WHERE key = ?", (key,))
if cursor.fetchone()[0] == 0:
cursor.execute("""
INSERT INTO ItemTable (key, value)
VALUES (?, ?)
""", (key, value))
else:
cursor.execute("""
UPDATE ItemTable SET value = ?
WHERE key = ?
""", (value, key))
print(f"{EMOJI['INFO']} {Fore.CYAN} {self.translator.get('auth.updating_pair')} {key.split('/')[-1]}...{Style.RESET_ALL}")
cursor.execute("COMMIT")
print(f"{EMOJI['SUCCESS']} {Fore.GREEN}{self.translator.get('auth.database_updated_successfully')}{Style.RESET_ALL}")
return True
except Exception as e:
cursor.execute("ROLLBACK")
raise e
except sqlite3.Error as e:
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.database_error', error=str(e))}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"\n{EMOJI['ERROR']} {Fore.RED} {self.translator.get('auth.an_error_occurred', error=str(e))}{Style.RESET_ALL}")
return False
finally:
if conn:
conn.close()
print(f"{EMOJI['DB']} {Fore.CYAN} {self.translator.get('auth.database_connection_closed')}{Style.RESET_ALL}")

View File

@@ -1,263 +0,0 @@
import os
from colorama import Fore, Style, init
import time
import random
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'FORM': '📝',
'VERIFY': '🔄',
'PASSWORD': '🔑',
'CODE': '📱',
'DONE': '',
'ERROR': '',
'WAIT': '',
'SUCCESS': '',
'MAIL': '📧',
'KEY': '🔐',
'UPDATE': '🔄',
'INFO': ''
}
class CursorRegistration:
def __init__(self, translator=None):
self.translator = translator
# Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.controller = None
self.mail_url = "https://yopmail.com/zh/email-generator"
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
self.settings_url = "https://www.cursor.com/settings"
self.email_address = None
self.signup_tab = None
self.email_tab = None
# Account information
self.password = self._generate_password()
# Generate first name and last name separately
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):
"""Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def setup_email(self):
"""Setup Email"""
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.browser_start')}...{Style.RESET_ALL}")
# Create a temporary email using new_tempemail, passing translator
from new_tempemail import NewTempEmail
self.temp_email = NewTempEmail(self.translator) # Pass translator
# Create a temporary email
email_address = self.temp_email.create_email()
if not email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_create_failed')}{Style.RESET_ALL}")
return False
# Save email address
self.email_address = email_address
self.email_tab = self.temp_email # Pass NewTempEmail instance
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}")
return False
def register_cursor(self):
"""注册 Cursor"""
browser_tab = None
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
# Directly use new_signup.py to sign up
from new_signup import main as new_signup_main
# Execute the new registration process, passing translator
result, browser_tab = new_signup_main(
email=self.email_address,
password=self.password,
first_name=self.first_name,
last_name=self.last_name,
email_tab=self.email_tab,
controller=self.controller,
translator=self.translator
)
if result:
# Use the returned browser instance to get account information
self.signup_tab = browser_tab # Save browser instance
success = self._get_account_info()
# Close browser after getting information
if browser_tab:
try:
browser_tab.quit()
except:
pass
return success
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
return False
finally:
# Ensure browser is closed in any case
if browser_tab:
try:
browser_tab.quit()
except:
pass
def _get_account_info(self):
"""Get Account Information and Token"""
try:
self.signup_tab.get(self.settings_url)
time.sleep(2)
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = self.signup_tab.ele(usage_selector)
total_usage = "未知"
if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip()
print(f"{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}")
max_attempts = 30
retry_interval = 2
attempts = 0
while attempts < max_attempts:
try:
cookies = self.signup_tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = cookie["value"].split("%3A%3A")[1]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
self._save_account_info(token, total_usage)
return True
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}")
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}")
return False
def _save_account_info(self, token, total_usage):
"""Save Account Information to File"""
try:
# Update authentication information first
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
# Reset machine ID
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
resetter = MachineIDResetter(self.translator) # Pass translator when creating instance
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID")
# Save account information to file
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {self.email_address}\n")
f.write(f"Password: {self.password}\n")
f.write(f"Token: {token}\n")
f.write(f"Usage Limit: {total_usage}\n")
f.write(f"{'='*50}\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")
return False
def start(self):
"""Start Registration Process"""
try:
if self.setup_email():
if self.register_cursor():
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
return True
return False
finally:
# Close email tab
if hasattr(self, 'temp_email'):
try:
self.temp_email.close()
except:
pass
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
"""Update Cursor Auth Info"""
auth_manager = CursorAuth(translator=self.translator)
return auth_manager.update_auth(email, access_token, refresh_token)
def main(translator=None):
"""Main function to be called from main.py"""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
registration = CursorRegistration(translator)
registration.start()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
main(main_translator)

View File

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

View File

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

View File

@@ -1,270 +0,0 @@
import os
from colorama import Fore, Style, init
import time
import random
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
os.environ["PYTHONVERBOSE"] = "0"
os.environ["PYINSTALLER_VERBOSE"] = "0"
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'FORM': '📝',
'VERIFY': '🔄',
'PASSWORD': '🔑',
'CODE': '📱',
'DONE': '',
'ERROR': '',
'WAIT': '',
'SUCCESS': '',
'MAIL': '📧',
'KEY': '🔐',
'UPDATE': '🔄',
'INFO': ''
}
class CursorRegistration:
def __init__(self, translator=None):
self.translator = translator
# Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.controller = None
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"
self.settings_url = "https://www.cursor.com/settings"
self.email_address = None
self.signup_tab = None
self.email_tab = None
# Generate account information
self.password = self._generate_password()
# Generate first name and last name separately
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):
"""Generate Random Password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def setup_email(self):
"""Setup Email"""
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.manual_email_input') if self.translator else 'Please enter your email address:'}")
self.email_address = input().strip()
if '@' not in self.email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_email') if self.translator else '无效的邮箱地址'}{Style.RESET_ALL}")
return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}\n{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.email_setup_failed', error=str(e))}{Style.RESET_ALL}")
return False
def get_verification_code(self):
"""Manually Get Verification Code"""
try:
print(f"{Fore.CYAN}{EMOJI['CODE']} {self.translator.get('register.manual_code_input') if self.translator else 'Please enter the verification code:'}")
code = input().strip()
if not code.isdigit() or len(code) != 6:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.invalid_code') if self.translator else '无效的验证码'}{Style.RESET_ALL}")
return None
return code
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.code_input_failed', error=str(e))}{Style.RESET_ALL}")
return None
def register_cursor(self):
"""Register Cursor"""
browser_tab = None
try:
print(f"{Fore.CYAN}{EMOJI['START']} {self.translator.get('register.register_start')}...{Style.RESET_ALL}")
# Use new_signup.py directly for registration
from new_signup import main as new_signup_main
# Execute new registration process, passing translator
result, browser_tab = new_signup_main(
email=self.email_address,
password=self.password,
first_name=self.first_name,
last_name=self.last_name,
email_tab=None, # No email tab needed
controller=self, # Pass self instead of self.controller
translator=self.translator
)
if result:
# Use the returned browser instance to get account information
self.signup_tab = browser_tab # Save browser instance
success = self._get_account_info()
# Close browser after getting information
if browser_tab:
try:
browser_tab.quit()
except:
pass
return success
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.register_process_error', error=str(e))}{Style.RESET_ALL}")
return False
finally:
# Ensure browser is closed in any case
if browser_tab:
try:
browser_tab.quit()
except:
pass
def _get_account_info(self):
"""Get Account Information and Token"""
try:
self.signup_tab.get(self.settings_url)
time.sleep(2)
usage_selector = (
"css:div.col-span-2 > div > div > div > div > "
"div:nth-child(1) > div.flex.items-center.justify-between.gap-2 > "
"span.font-mono.text-sm\\/\\[0\\.875rem\\]"
)
usage_ele = self.signup_tab.ele(usage_selector)
total_usage = "未知"
if usage_ele:
total_usage = usage_ele.text.split("/")[-1].strip()
print(f"Total Usage: {total_usage}\n")
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('register.get_token')}...{Style.RESET_ALL}")
max_attempts = 30
retry_interval = 2
attempts = 0
while attempts < max_attempts:
try:
cookies = self.signup_tab.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
token = cookie["value"].split("%3A%3A")[1]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.token_success')}{Style.RESET_ALL}")
self._save_account_info(token, total_usage)
return True
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_max_attempts', max=max_attempts)}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.token_failed', error=str(e))}{Style.RESET_ALL}")
attempts += 1
if attempts < max_attempts:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} {self.translator.get('register.token_attempt', attempt=attempts, time=retry_interval)}{Style.RESET_ALL}")
time.sleep(retry_interval)
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.account_error', error=str(e))}{Style.RESET_ALL}")
return False
def _save_account_info(self, token, total_usage):
"""Save Account Information to File"""
try:
# Update authentication information first
print(f"{Fore.CYAN}{EMOJI['KEY']} {self.translator.get('register.update_cursor_auth_info')}...{Style.RESET_ALL}")
if self.update_cursor_auth(email=self.email_address, access_token=token, refresh_token=token):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.cursor_auth_info_updated')}...{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.cursor_auth_info_update_failed')}...{Style.RESET_ALL}")
# Reset machine ID
print(f"{Fore.CYAN}{EMOJI['UPDATE']} {self.translator.get('register.reset_machine_id')}...{Style.RESET_ALL}")
resetter = MachineIDResetter(self.translator) # Create instance with translator
if not resetter.reset_machine_ids(): # Call reset_machine_ids method directly
raise Exception("Failed to reset machine ID")
# Save account information to file
with open('cursor_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {self.email_address}\n")
f.write(f"Password: {self.password}\n")
f.write(f"Token: {token}\n")
f.write(f"Usage Limit: {total_usage}\n")
f.write(f"{'='*50}\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('register.account_info_saved')}...{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('register.save_account_info_failed', error=str(e))}{Style.RESET_ALL}")
return False
def start(self):
"""Start Registration Process"""
try:
if self.setup_email():
if self.register_cursor():
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {self.translator.get('register.cursor_registration_completed')}...{Style.RESET_ALL}")
return True
return False
finally:
# Close email tab
if hasattr(self, 'temp_email'):
try:
self.temp_email.close()
except:
pass
def update_cursor_auth(self, email=None, access_token=None, refresh_token=None):
"""Convenient function to update Cursor authentication information"""
auth_manager = CursorAuth(translator=self.translator)
return auth_manager.update_auth(email, access_token, refresh_token)
def main(translator=None):
"""Main function to be called from main.py"""
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['START']} {translator.get('register.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
registration = CursorRegistration(translator)
registration.start()
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
input(f"{EMOJI['INFO']} {translator.get('register.press_enter')}...")
if __name__ == "__main__":
from main import translator as main_translator
main(main_translator)

View File

@@ -1,386 +0,0 @@
from oauth_auth import OAuthHandler
import time
from colorama import Fore, Style, init
import sys
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'DELETE': '🗑️',
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': '',
'WARNING': '⚠️'
}
class CursorGoogleAccountDeleter(OAuthHandler):
def __init__(self, translator=None):
super().__init__(translator, auth_type='google')
def delete_google_account(self):
"""Delete Cursor account using Google OAuth"""
try:
# Setup browser and select profile
if not self.setup_browser():
return False
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.starting_process') if self.translator else 'Starting account deletion process...'}{Style.RESET_ALL}")
# Navigate to Cursor auth page - using the same URL as in registration
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(2)
# Click Google auth button using same selectors as in registration
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
]
auth_btn = None
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn:
break
except:
continue
if not auth_btn:
raise Exception(self.translator.get('account_delete.google_button_not_found') if self.translator else "Google login button not found")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.logging_in') if self.translator else 'Logging in with Google...'}{Style.RESET_ALL}")
auth_btn.click()
# Wait for authentication to complete using a more robust method
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('account_delete.waiting_for_auth', fallback='Waiting for Google authentication...')}{Style.RESET_ALL}")
# Dynamic wait for authentication
max_wait_time = 120 # Increase maximum wait time to 120 seconds
start_time = time.time()
check_interval = 3 # Check every 3 seconds
google_account_alert_shown = False # Track if we've shown the alert already
while time.time() - start_time < max_wait_time:
current_url = self.browser.url
# If we're already on the settings or dashboard page, we're successful
if "/dashboard" in current_url or "/settings" in current_url or "cursor.com" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.login_successful') if self.translator else 'Login successful'}{Style.RESET_ALL}")
break
# If we're on Google accounts page or accounts.google.com, wait for user selection
if "accounts.google.com" in current_url:
# Only show the alert once to avoid spamming
if not google_account_alert_shown:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.select_google_account', fallback='Please select your Google account...')}{Style.RESET_ALL}")
# Alert to indicate user action needed
try:
self.browser.run_js("""
alert('Please select your Google account to continue with Cursor authentication');
""")
google_account_alert_shown = True # Mark that we've shown the alert
except:
pass # Alert is optional
# Sleep before checking again
time.sleep(check_interval)
else:
# If the loop completed without breaking, it means we hit the timeout
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.auth_timeout', fallback='Authentication timeout, continuing anyway...')}{Style.RESET_ALL}")
# Check current URL to determine next steps
current_url = self.browser.url
# If we're already on the settings page, no need to navigate
if "/settings" in current_url and "cursor.com" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.already_on_settings', fallback='Already on settings page')}{Style.RESET_ALL}")
# If we're on the dashboard or any Cursor page but not settings, navigate to settings
elif "cursor.com" in current_url or "authenticator.cursor.sh" in current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.navigating_to_settings', fallback='Navigating to settings page...')}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
# If we're still on Google auth or somewhere else, try directly going to settings
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.login_redirect_failed', fallback='Login redirection failed, trying direct navigation...')}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
# Wait for the settings page to load
time.sleep(3) # Reduced from 5 seconds
# First look for the email element to confirm we're logged in
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.found_email', email=email, fallback=f'Found email: {email}')}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.email_not_found', error=str(e), fallback=f'Email not found: {str(e)}')}{Style.RESET_ALL}")
# Click on "Advanced" tab or dropdown - keep only the successful approach
advanced_found = False
# Direct JavaScript querySelector approach that worked according to logs
try:
advanced_element_js = self.browser.run_js("""
// Try to find the Advanced dropdown using querySelector with the exact classes
let advancedElement = document.querySelector('div.mb-0.flex.cursor-pointer.items-center.text-xs:not([style*="display: none"])');
// If not found, try a more general approach
if (!advancedElement) {
const allDivs = document.querySelectorAll('div');
for (const div of allDivs) {
if (div.textContent.includes('Advanced') &&
div.className.includes('mb-0') &&
div.className.includes('flex') &&
div.className.includes('cursor-pointer')) {
advancedElement = div;
break;
}
}
}
// Click the element if found
if (advancedElement) {
advancedElement.click();
return true;
}
return false;
""")
if advanced_element_js:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.advanced_tab_clicked', fallback='Found and clicked Advanced using direct JavaScript selector')}{Style.RESET_ALL}")
advanced_found = True
time.sleep(1) # Reduced from 2 seconds
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.advanced_tab_error', error=str(e), fallback='JavaScript querySelector approach failed: {str(e)}')}{Style.RESET_ALL}")
if not advanced_found:
# Fallback to direct URL navigation which is faster and more reliable
try:
self.browser.get("https://www.cursor.com/settings?tab=advanced")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('account_delete.direct_advanced_navigation', fallback='Trying direct navigation to advanced tab')}{Style.RESET_ALL}")
advanced_found = True
except:
raise Exception(self.translator.get('account_delete.advanced_tab_not_found') if self.translator else "Advanced option not found after multiple attempts")
# Wait for dropdown/tab content to load
time.sleep(2) # Reduced from 4 seconds
# Find and click the "Delete Account" button
delete_button_found = False
# Simplified approach for delete button based on what worked
delete_button_selectors = [
'xpath://button[contains(., "Delete Account")]',
'xpath://button[text()="Delete Account"]',
'xpath://div[contains(text(), "Delete Account")]',
'xpath://button[contains(text(), "Delete") and contains(text(), "Account")]'
]
for selector in delete_button_selectors:
try:
delete_button = self.browser.ele(selector, timeout=2)
if delete_button:
delete_button.click()
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('account_delete.delete_button_clicked') if self.translator else 'Clicked on Delete Account button'}{Style.RESET_ALL}")
delete_button_found = True
break
except:
continue
if not delete_button_found:
raise Exception(self.translator.get('account_delete.delete_button_not_found') if self.translator else "Delete Account button not found")
# Wait for confirmation dialog to appear
time.sleep(2)
# Check if we need to input "Delete" at all - some modals might not require it
input_required = True
try:
# Try detecting if the DELETE button is already enabled
delete_button_enabled = self.browser.run_js("""
const buttons = Array.from(document.querySelectorAll('button'));
const deleteButtons = buttons.filter(btn =>
btn.textContent.trim() === 'DELETE' ||
btn.textContent.trim() === 'Delete'
);
if (deleteButtons.length > 0) {
return !deleteButtons.some(btn => btn.disabled);
}
return false;
""")
if delete_button_enabled:
print(f"{Fore.CYAN}{EMOJI['INFO']} DELETE button appears to be enabled already. Input may not be required.{Style.RESET_ALL}")
input_required = False
except:
pass
# Type "Delete" in the confirmation input - only if required
delete_input_found = False
if input_required:
# Try common selectors for the input field
delete_input_selectors = [
'xpath://input[@placeholder="Delete"]',
'xpath://div[contains(@class, "modal")]//input',
'xpath://input',
'css:input'
]
for selector in delete_input_selectors:
try:
delete_input = self.browser.ele(selector, timeout=3)
if delete_input:
delete_input.clear()
delete_input.input("Delete")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete', fallback='Typed \"Delete\" in confirmation box')}{Style.RESET_ALL}")
delete_input_found = True
time.sleep(2)
break
except:
# Try direct JavaScript input as fallback
try:
self.browser.run_js(f"""
arguments[0].value = "Delete";
const event = new Event('input', {{ bubbles: true }});
arguments[0].dispatchEvent(event);
const changeEvent = new Event('change', {{ bubbles: true }});
arguments[0].dispatchEvent(changeEvent);
""", delete_input)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.typed_delete_js', fallback='Typed \"Delete\" using JavaScript')}{Style.RESET_ALL}")
delete_input_found = True
time.sleep(2)
break
except:
continue
if not delete_input_found:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('account_delete.delete_input_not_found', fallback='Delete confirmation input not found, continuing anyway')}{Style.RESET_ALL}")
time.sleep(2)
# Wait before clicking the final DELETE button
time.sleep(2)
# Click on the final DELETE button
confirm_button_found = False
# Use JavaScript approach for the DELETE button
try:
delete_button_js = self.browser.run_js("""
// Try to find the DELETE button by exact text content
const buttons = Array.from(document.querySelectorAll('button'));
const deleteButton = buttons.find(btn =>
btn.textContent.trim() === 'DELETE' ||
btn.textContent.trim() === 'Delete'
);
if (deleteButton) {
console.log("Found DELETE button with JavaScript");
deleteButton.click();
return true;
}
// If not found by text, try to find right-most button in the modal
const modalButtons = Array.from(document.querySelectorAll('.relative button, [role="dialog"] button, .modal button, [aria-modal="true"] button'));
if (modalButtons.length > 1) {
modalButtons.sort((a, b) => {
const rectA = a.getBoundingClientRect();
const rectB = b.getBoundingClientRect();
return rectB.right - rectA.right;
});
console.log("Clicking right-most button in modal");
modalButtons[0].click();
return true;
} else if (modalButtons.length === 1) {
console.log("Clicking single button found in modal");
modalButtons[0].click();
return true;
}
return false;
""")
if delete_button_js:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Clicked DELETE button')}{Style.RESET_ALL}")
confirm_button_found = True
except:
pass
if not confirm_button_found:
# Fallback to simple selectors
delete_button_selectors = [
'xpath://button[text()="DELETE"]',
'xpath://div[contains(@class, "modal")]//button[last()]'
]
for selector in delete_button_selectors:
try:
delete_button = self.browser.ele(selector, timeout=2)
if delete_button:
delete_button.click()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('account_delete.delete_button_clicked', fallback='Account deleted successfully!')}{Style.RESET_ALL}")
confirm_button_found = True
break
except:
continue
if not confirm_button_found:
raise Exception(self.translator.get('account_delete.confirm_button_not_found') if self.translator else "Confirm button not found")
# Wait a moment to see the confirmation
time.sleep(2)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('account_delete.error', error=str(e)) if self.translator else f'Error during account deletion: {str(e)}'}{Style.RESET_ALL}")
return False
finally:
# Clean up browser
if self.browser:
try:
self.browser.quit()
except:
pass
def main(translator=None):
"""Main function to handle Google account deletion"""
print(f"\n{Fore.CYAN}{EMOJI['START']} {translator.get('account_delete.title') if translator else 'Cursor Google Account Deletion Tool'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{'' * 50}{Style.RESET_ALL}")
deleter = CursorGoogleAccountDeleter(translator)
try:
# Ask for confirmation
print(f"{Fore.RED}{EMOJI['WARNING']} {translator.get('account_delete.warning') if translator else 'WARNING: This will permanently delete your Cursor account. This action cannot be undone.'}{Style.RESET_ALL}")
confirm = input(f"{Fore.RED} {translator.get('account_delete.confirm_prompt') if translator else 'Are you sure you want to proceed? (y/N): '}{Style.RESET_ALL}").lower()
if confirm != 'y':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.cancelled') if translator else 'Account deletion cancelled.'}{Style.RESET_ALL}")
return
success = deleter.delete_google_account()
if success:
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('account_delete.success') if translator else 'Your Cursor account has been successfully deleted!'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.failed') if translator else 'Account deletion process failed or was cancelled.'}{Style.RESET_ALL}")
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('account_delete.interrupted') if translator else 'Account deletion process interrupted by user.'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('account_delete.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
finally:
print(f"{Fore.YELLOW}{'' * 50}{Style.RESET_ALL}")
if __name__ == "__main__":
main()

112
get_user_token.py Normal file
View File

@@ -0,0 +1,112 @@
import requests
import json
import time
from colorama import Fore, Style
import os
from config import get_config
# Define emoji constants
EMOJI = {
'START': '🚀',
'OAUTH': '🔑',
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': '',
'WARNING': '⚠️'
}
def refresh_token(token, translator=None):
"""Refresh the token using the Chinese server API
Args:
token (str): The full WorkosCursorSessionToken cookie value
translator: Optional translator object
Returns:
str: The refreshed access token or original token if refresh fails
"""
try:
config = get_config(translator)
# Get refresh_server URL from config or use default
refresh_server = config.get('Token', 'refresh_server', fallback='https://token.cursorpro.com.cn')
# Ensure the token is URL encoded properly
if '%3A%3A' not in token and '::' in token:
# Replace :: with URL encoded version if needed
token = token.replace('::', '%3A%3A')
# Make the request to the refresh server
url = f"{refresh_server}/reftoken?token={token}"
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('token.refreshing') if translator else 'Refreshing token...'}{Style.RESET_ALL}")
response = requests.get(url, timeout=30)
if response.status_code == 200:
try:
data = response.json()
if data.get('code') == 0 and data.get('msg') == "获取成功":
access_token = data.get('data', {}).get('accessToken')
days_left = data.get('data', {}).get('days_left', 0)
expire_time = data.get('data', {}).get('expire_time', 'Unknown')
if access_token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('token.refresh_success', days=days_left, expire=expire_time) if translator else f'Token refreshed successfully! Valid for {days_left} days (expires: {expire_time})'}{Style.RESET_ALL}")
return access_token
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('token.no_access_token') if translator else 'No access token in response'}{Style.RESET_ALL}")
else:
error_msg = data.get('msg', 'Unknown error')
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.refresh_failed', error=error_msg) if translator else f'Token refresh failed: {error_msg}'}{Style.RESET_ALL}")
except json.JSONDecodeError:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.invalid_response') if translator else 'Invalid JSON response from refresh server'}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.server_error', status=response.status_code) if translator else f'Refresh server error: HTTP {response.status_code}'}{Style.RESET_ALL}")
except requests.exceptions.Timeout:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.request_timeout') if translator else 'Request to refresh server timed out'}{Style.RESET_ALL}")
except requests.exceptions.ConnectionError:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.connection_error') if translator else 'Connection error to refresh server'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.unexpected_error', error=str(e)) if translator else f'Unexpected error during token refresh: {str(e)}'}{Style.RESET_ALL}")
# Return original token if refresh fails
return token.split('%3A%3A')[-1] if '%3A%3A' in token else token.split('::')[-1] if '::' in token else token
def get_token_from_cookie(cookie_value, translator=None):
"""Extract and process token from cookie value
Args:
cookie_value (str): The WorkosCursorSessionToken cookie value
translator: Optional translator object
Returns:
str: The processed token
"""
try:
# Try to refresh the token with the API first
refreshed_token = refresh_token(cookie_value, translator)
# If refresh succeeded and returned a different token, use it
if refreshed_token and refreshed_token != cookie_value:
return refreshed_token
# If refresh failed or returned same token, use traditional extraction method
if '%3A%3A' in cookie_value:
return cookie_value.split('%3A%3A')[-1]
elif '::' in cookie_value:
return cookie_value.split('::')[-1]
else:
return cookie_value
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.extraction_error', error=str(e)) if translator else f'Error extracting token: {str(e)}'}{Style.RESET_ALL}")
# Fall back to original behavior
if '%3A%3A' in cookie_value:
return cookie_value.split('%3A%3A')[-1]
elif '::' in cookie_value:
return cookie_value.split('::')[-1]
else:
return cookie_value

View File

@@ -1,701 +0,0 @@
import os
import time
import uuid
import json
import random
import string
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import logging
import platform
from colorama import Fore, Style, init
from selenium.common.exceptions import TimeoutException, WebDriverException, NoSuchElementException
import shutil
# Initialize colorama
init()
# Set up logging
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
# Define emoji constants
EMOJI = {
'START': '🚀',
'FORM': '📝',
'VERIFY': '🔄',
'PASSWORD': '🔑',
'CODE': '📱',
'DONE': '',
'ERROR': '',
'WAIT': '',
'SUCCESS': '',
'MAIL': '📧',
'KEY': '🔐',
'UPDATE': '🔄',
'INFO': '',
'EMAIL': '📧',
'REFRESH': '🔄',
'LINK': '🔗',
'WARNING': '⚠️'
}
class GitHubCursorRegistration:
def __init__(self, translator=None):
self.translator = translator
# Set browser to visible mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.email_address = None
# Generate random credentials
self.github_username = ''.join(random.choices(string.ascii_lowercase + string.digits, k=10))
self.github_password = ''.join(random.choices(string.ascii_letters + string.digits + string.punctuation, k=16))
def setup_browser(self):
"""Setup and configure the web browser"""
try:
print(f"{Fore.CYAN}{EMOJI['START']} Setting up browser...{Style.RESET_ALL}")
options = Options()
options.add_argument('--incognito')
options.add_argument('--no-sandbox')
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--window-size=1920,1080')
options.add_argument('--disable-notifications')
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36')
self.browser = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options)
self.browser.set_page_load_timeout(30)
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to setup browser: {str(e)}{Style.RESET_ALL}")
return False
def get_temp_email(self):
"""Get a temporary email address using YOPmail"""
try:
if not self.browser:
if not self.setup_browser():
return False
print(f"{Fore.CYAN}{EMOJI['MAIL']} Generating temporary email address...{Style.RESET_ALL}")
self.browser.get("https://yopmail.com/")
time.sleep(2)
# Generate a realistic username
first_names = ["john", "sara", "michael", "emma", "david", "jennifer", "robert", "lisa"]
last_names = ["smith", "johnson", "williams", "brown", "jones", "miller", "davis", "garcia"]
random_first = random.choice(first_names)
random_last = random.choice(last_names)
random_num = random.randint(100, 999)
username = f"{random_first}.{random_last}{random_num}"
# Enter the username and check inbox
email_field = self.browser.find_element(By.XPATH, "//input[@id='login']")
if email_field:
email_field.clear()
email_field.send_keys(username)
time.sleep(1)
# Click the check button
check_button = self.browser.find_element(By.XPATH, "//button[@title='Check Inbox' or @class='sbut' or contains(@onclick, 'ver')]")
if check_button:
check_button.click()
time.sleep(2)
self.email_address = f"{username}@yopmail.com"
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Temp email created: {self.email_address}{Style.RESET_ALL}")
return True
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to create YOPmail address{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error getting temporary email: {str(e)}{Style.RESET_ALL}")
return False
def register_github(self):
"""Register a new GitHub account"""
if not self.email_address:
print(f"{Fore.RED}{EMOJI['ERROR']} No email address available{Style.RESET_ALL}")
return False
if not self.browser:
if not self.setup_browser():
return False
try:
print(f"{Fore.CYAN}{EMOJI['FORM']} Registering GitHub account...{Style.RESET_ALL}")
self.browser.get("https://github.com/join")
time.sleep(3)
# Fill in the registration form
WebDriverWait(self.browser, 15).until(EC.visibility_of_element_located((By.ID, "user_login")))
self.browser.find_element(By.ID, "user_login").send_keys(self.github_username)
self.browser.find_element(By.ID, "user_email").send_keys(self.email_address)
self.browser.find_element(By.ID, "user_password").send_keys(self.github_password)
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub username: {self.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} GitHub password: {self.github_password}{Style.RESET_ALL}")
# Check for any notice or popup and handle it
try:
signup_button = self.browser.find_element(By.ID, "signup_button")
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking sign up button...{Style.RESET_ALL}")
signup_button.click()
except NoSuchElementException:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Signup button not found, trying alternative selector{Style.RESET_ALL}")
buttons = self.browser.find_elements(By.TAG_NAME, "button")
for button in buttons:
if "Sign up" in button.text:
button.click()
break
# Wait for page transition and check for CAPTCHA
time.sleep(5)
# Check if registration was successful or if CAPTCHA appeared
current_url = self.browser.current_url
# Look for CAPTCHA in URL or on page
if "captcha" in current_url.lower() or "are you a robot" in self.browser.page_source.lower():
print(f"{Fore.YELLOW}{EMOJI['WAIT']} CAPTCHA detected, please complete it manually{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You have 60 seconds to solve the CAPTCHA...{Style.RESET_ALL}")
# Wait for user to solve CAPTCHA (60 seconds max)
for i in range(60):
current_url = self.browser.current_url
if "captcha" not in current_url.lower() and "are you a robot" not in self.browser.page_source.lower():
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} CAPTCHA completed successfully{Style.RESET_ALL}")
break
time.sleep(1)
if i % 10 == 0 and i > 0:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} Still waiting for CAPTCHA completion... {60-i} seconds remaining{Style.RESET_ALL}")
# Check if CAPTCHA was solved after waiting
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA not solved within time limit{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want more time to solve the CAPTCHA? (yes/no){Style.RESET_ALL}")
response = input().lower().strip()
if response in ['yes', 'y']:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've completed the CAPTCHA...{Style.RESET_ALL}")
input()
if "captcha" in self.browser.current_url.lower() or "are you a robot" in self.browser.page_source.lower():
print(f"{Fore.RED}{EMOJI['ERROR']} CAPTCHA still not solved{Style.RESET_ALL}")
return False
else:
return False
# Wait for registration to complete
time.sleep(5)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub account registered{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register GitHub account: {str(e)}{Style.RESET_ALL}")
return False
def check_email_verification(self):
"""Check for GitHub verification email and click the verification link"""
if not self.email_address or not self.browser:
print(f"{Fore.RED}{EMOJI['ERROR']} Email or browser not available{Style.RESET_ALL}")
return False
try:
print(f"{Fore.CYAN}{EMOJI['EMAIL']} Checking for verification email...{Style.RESET_ALL}")
# Extract username from email for YOPmail
username = self.email_address.split('@')[0]
max_attempts = 10
for attempt in range(1, max_attempts + 1):
print(f"{Fore.CYAN}{EMOJI['REFRESH']} Checking YOPmail inbox (attempt {attempt}/{max_attempts})...{Style.RESET_ALL}")
# Go to YOPmail inbox
self.browser.get(f"https://yopmail.com/en/wm")
time.sleep(2)
# Enter email address
try:
email_input = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "login"))
)
email_input.clear()
email_input.send_keys(username)
# Click the check inbox button
check_button = self.browser.find_element(By.CSS_SELECTOR, "button[onclick='verif()']")
check_button.click()
time.sleep(3)
# Switch to inbox frame
iframe = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "ifinbox"))
)
self.browser.switch_to.frame(iframe)
# Look for GitHub email
emails = self.browser.find_elements(By.CSS_SELECTOR, "div.m")
github_email = None
for email in emails:
if "github" in email.text.lower():
github_email = email
break
if github_email:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} GitHub verification email found{Style.RESET_ALL}")
github_email.click()
time.sleep(2)
# Switch back to default content
self.browser.switch_to.default_content()
# Switch to email content frame
iframe = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "ifmail"))
)
self.browser.switch_to.frame(iframe)
# Find verification link
try:
# Look for the verification button or link
verification_elements = self.browser.find_elements(By.XPATH, "//a[contains(text(), 'Verify') or contains(text(), 'verify') or contains(@href, 'verify')]")
if verification_elements:
verification_link = verification_elements[0].get_attribute('href')
print(f"{Fore.CYAN}{EMOJI['LINK']} Found verification link{Style.RESET_ALL}")
# Open the verification link in the same window
self.browser.get(verification_link)
time.sleep(5)
# Check if verification was successful
if "verified" in self.browser.page_source.lower() or "successful" in self.browser.page_source.lower():
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Email verified successfully{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Email verification page loaded but success not confirmed{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check if verification was successful manually and press Enter to continue...{Style.RESET_ALL}")
input()
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} No verification link found in email{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error extracting verification link: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}{EMOJI['WAIT']} No GitHub verification email yet, waiting... ({attempt}/{max_attempts}){Style.RESET_ALL}")
time.sleep(15) # Wait before checking again
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error checking email: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} No verification email received after {max_attempts} attempts{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to check manually? (yes/no){Style.RESET_ALL}")
response = input().lower().strip()
if response in ['yes', 'y']:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Please check your YOPmail inbox manually at: https://yopmail.com/en/wm")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Username: {username}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Press Enter when you've verified the email...{Style.RESET_ALL}")
input()
return True
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to check verification email: {str(e)}{Style.RESET_ALL}")
return False
def register_cursor(self):
"""Register with Cursor using GitHub"""
if not self.browser:
if not self.setup_browser():
return False
try:
print(f"{Fore.CYAN}{EMOJI['KEY']} Registering with Cursor using GitHub...{Style.RESET_ALL}")
# Navigate to Cursor login page
self.browser.get("https://cursor.sh/login")
time.sleep(3)
try:
# Look for GitHub login button
github_buttons = WebDriverWait(self.browser, 15).until(
EC.presence_of_all_elements_located((By.XPATH, "//button[contains(., 'GitHub') or contains(@class, 'github')]"))
)
if not github_buttons:
print(f"{Fore.RED}{EMOJI['ERROR']} GitHub login button not found{Style.RESET_ALL}")
return False
# Click the first GitHub button
print(f"{Fore.CYAN}{EMOJI['INFO']} Clicking GitHub login button...{Style.RESET_ALL}")
github_buttons[0].click()
time.sleep(5)
# Check if we're redirected to GitHub login
current_url = self.browser.current_url
if "github.com" in current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Redirected to GitHub login{Style.RESET_ALL}")
# Check if we need to log in to GitHub
if "login" in current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Logging into GitHub...{Style.RESET_ALL}")
try:
# Enter GitHub credentials
username_field = WebDriverWait(self.browser, 10).until(
EC.presence_of_element_located((By.ID, "login_field"))
)
username_field.send_keys(self.github_username)
password_field = self.browser.find_element(By.ID, "password")
password_field.send_keys(self.github_password)
# Click sign in
signin_button = self.browser.find_element(By.CSS_SELECTOR, "input[type='submit']")
signin_button.click()
time.sleep(5)
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub login: {str(e)}{Style.RESET_ALL}")
return False
# Check if we're on the authorization page
if "authorize" in self.browser.current_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} Authorizing Cursor app...{Style.RESET_ALL}")
try:
# Look for authorization button
auth_buttons = self.browser.find_elements(By.XPATH, "//button[contains(., 'Authorize') or contains(@class, 'btn-primary')]")
if auth_buttons:
auth_buttons[0].click()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor authorized with GitHub{Style.RESET_ALL}")
time.sleep(5)
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No authorization button found, GitHub may be already authorized{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error during GitHub authorization: {str(e)}{Style.RESET_ALL}")
# Wait for Cursor dashboard to load
timeout = 30
start_time = time.time()
while time.time() - start_time < timeout:
if "cursor.sh" in self.browser.current_url and not "login" in self.browser.current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Successfully logged into Cursor{Style.RESET_ALL}")
break
time.sleep(1)
if "login" in self.browser.current_url:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to log into Cursor after {timeout} seconds{Style.RESET_ALL}")
return False
# Wait for dashboard elements to load
time.sleep(3)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Cursor registered with GitHub successfully{Style.RESET_ALL}")
# Now reset the machine ID
return self.reset_machine_id()
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Error during Cursor registration: {str(e)}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to register with Cursor: {str(e)}{Style.RESET_ALL}")
return False
def reset_machine_id(self):
"""Reset the Cursor machine ID to bypass limitations"""
try:
print(f"{Fore.CYAN}{EMOJI['UPDATE']} Resetting Cursor machine ID...{Style.RESET_ALL}")
# Find Cursor app data location based on platform
cursor_data_dir = None
if platform.system() == "Windows":
appdata = os.getenv('APPDATA')
if appdata:
cursor_data_dir = os.path.join(appdata, "cursor", "Local Storage", "leveldb")
elif platform.system() == "Darwin": # macOS
home = os.path.expanduser("~")
cursor_data_dir = os.path.join(home, "Library", "Application Support", "cursor", "Local Storage", "leveldb")
elif platform.system() == "Linux":
home = os.path.expanduser("~")
cursor_data_dir = os.path.join(home, ".config", "cursor", "Local Storage", "leveldb")
if not cursor_data_dir or not os.path.exists(cursor_data_dir):
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found at: {cursor_data_dir}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reset the machine ID manually{Style.RESET_ALL}")
# Try to find the Cursor data directory
if platform.system() == "Linux":
possible_paths = [
os.path.join(os.path.expanduser("~"), ".config", "cursor"),
os.path.join(os.path.expanduser("~"), ".cursor")
]
for path in possible_paths:
if os.path.exists(path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor directory at: {path}{Style.RESET_ALL}")
# Look for Local Storage subfolder
for root, dirs, files in os.walk(path):
if "Local Storage" in dirs:
cursor_data_dir = os.path.join(root, "Local Storage", "leveldb")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Found Cursor data directory at: {cursor_data_dir}{Style.RESET_ALL}")
break
break
if cursor_data_dir and os.path.exists(cursor_data_dir):
# Generate a new UUID
new_machine_id = str(uuid.uuid4())
print(f"{Fore.CYAN}{EMOJI['KEY']} New machine ID: {new_machine_id}{Style.RESET_ALL}")
# Ask for permission to modify files
print(f"{Fore.YELLOW}{EMOJI['WARNING']} This operation will modify Cursor app data files{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Do you want to continue? (yes/no){Style.RESET_ALL}")
response = input().lower().strip()
if response not in ['yes', 'y']:
print(f"{Fore.YELLOW}{EMOJI['INFO']} Machine ID reset aborted{Style.RESET_ALL}")
return False
# Backup the directory
backup_dir = cursor_data_dir + "_backup_" + time.strftime("%Y%m%d%H%M%S")
print(f"{Fore.CYAN}{EMOJI['INFO']} Creating backup of data directory to: {backup_dir}{Style.RESET_ALL}")
try:
shutil.copytree(cursor_data_dir, backup_dir)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Backup created successfully{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Failed to create backup: {str(e)}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} Continuing without backup...{Style.RESET_ALL}")
# Find and modify files containing the machine ID
modified = False
for filename in os.listdir(cursor_data_dir):
if filename.endswith(".log") or filename.endswith(".ldb"):
file_path = os.path.join(cursor_data_dir, filename)
try:
with open(file_path, "rb") as f:
content = f.read()
# Look for patterns that might contain machine ID
if b"machineId" in content:
print(f"{Fore.CYAN}{EMOJI['INFO']} Found machineId reference in: {filename}{Style.RESET_ALL}")
modified = True
# For safety, don't modify the binary files directly
# Instead, instruct user to uninstall and reinstall Cursor
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Binary files found that may contain machine ID{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} For best results, please:{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 1. Close Cursor if it's running{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 2. Uninstall Cursor completely{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 3. Reinstall Cursor{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} 4. Login with your new GitHub account{Style.RESET_ALL}")
break
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error processing file {filename}: {str(e)}{Style.RESET_ALL}")
if not modified:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} No machine ID references found in data files{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to reinstall Cursor for a complete reset{Style.RESET_ALL}")
# Save credentials before returning
self.save_credentials()
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Machine ID reset process completed{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Cursor data directory not found{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} You may need to manually reset the machine ID by reinstalling Cursor{Style.RESET_ALL}")
# Still save credentials
self.save_credentials()
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to reset machine ID: {str(e)}{Style.RESET_ALL}")
# Still save credentials even if machine ID reset fails
self.save_credentials()
return False
def save_credentials(self):
"""Save the generated credentials to a file"""
try:
if not self.email_address or not self.github_username or not self.github_password:
print(f"{Fore.RED}{EMOJI['ERROR']} No credentials to save{Style.RESET_ALL}")
return False
output_file = "github_cursor_accounts.txt"
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
credentials = {
"timestamp": timestamp,
"github_username": self.github_username,
"github_password": self.github_password,
"email": self.email_address
}
credentials_json = json.dumps(credentials)
# Check if file exists and create if not
file_exists = os.path.exists(output_file)
with open(output_file, "a") as f:
if not file_exists:
f.write("# GitHub + Cursor AI Accounts\n")
f.write("# Format: JSON with timestamp, github_username, github_password, email\n\n")
f.write(credentials_json + "\n")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Credentials saved to: {output_file}{Style.RESET_ALL}")
# Print a summary
print(f"\n{Fore.GREEN}{EMOJI['SUCCESS']} Registration Summary:{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • Saved to: {output_file}{Style.RESET_ALL}\n")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} Failed to save credentials: {str(e)}{Style.RESET_ALL}")
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} Make sure to copy these credentials manually:{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Username: {self.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • GitHub Password: {self.github_password}{Style.RESET_ALL}")
print(f"{Fore.CYAN} • Email Address: {self.email_address}{Style.RESET_ALL}\n")
return False
def cleanup(self):
"""Clean up resources"""
if self.browser:
try:
self.browser.quit()
except:
pass
def start_registration(self):
"""Start the GitHub Cursor registration process"""
try:
# Step 1: Get temporary email
if not self.get_temp_email():
return False
# Step 2: Register GitHub account
if not self.register_github():
return False
# Step 3: Check and verify email
if not self.check_email_verification():
return False
# Step 4: Register Cursor with GitHub
if not self.register_cursor():
return False
# Step 5: Reset machine ID
self.reset_machine_id()
return True
finally:
self.cleanup()
def display_features_and_warnings(translator=None):
"""Display features and warnings before proceeding"""
if translator:
print(f"\n🚀 {translator.get('github_register.title')}")
print("=====================================")
print(f"{translator.get('github_register.features_header')}:")
print(f" - {translator.get('github_register.feature1')}")
print(f" - {translator.get('github_register.feature2')}")
print(f" - {translator.get('github_register.feature3')}")
print(f" - {translator.get('github_register.feature4')}")
print(f" - {translator.get('github_register.feature5')}")
print(f" - {translator.get('github_register.feature6')}")
print(f"\n⚠️ {translator.get('github_register.warnings_header')}:")
print(f" - {translator.get('github_register.warning1')}")
print(f" - {translator.get('github_register.warning2')}")
print(f" - {translator.get('github_register.warning3')}")
print(f" - {translator.get('github_register.warning4')}")
print("=====================================\n")
else:
print("\n🚀 GitHub + Cursor AI Registration Automation")
print("=====================================")
print("Features:")
print(" - Creates a temporary email using YOPmail")
print(" - Registers a new GitHub account with random credentials")
print(" - Verifies the GitHub email automatically")
print(" - Logs into Cursor AI using GitHub authentication")
print(" - Resets the machine ID to bypass trial detection")
print(" - Saves all credentials to a file")
print("\n⚠️ Warnings:")
print(" - This script automates account creation, which may violate GitHub/Cursor terms of service")
print(" - Requires internet access and administrative privileges")
print(" - CAPTCHA or additional verification may interrupt automation")
print(" - Use responsibly and at your own risk")
print("=====================================\n")
def get_user_confirmation(translator=None):
"""Prompt the user for confirmation to proceed"""
while True:
if translator:
response = input(f"{translator.get('github_register.confirm')} (yes/no): ").lower().strip()
else:
response = input("Do you want to proceed with GitHub + Cursor AI registration? (yes/no): ").lower().strip()
if response in ['yes', 'y']:
return True
elif response in ['no', 'n']:
if translator:
print(f"{translator.get('github_register.cancelled')}")
else:
print("❌ Operation cancelled.")
return False
else:
if translator:
print(f"{translator.get('github_register.invalid_choice')}")
else:
print("Please enter 'yes' or 'no'.")
def main(translator=None):
"""Main function to run the GitHub Cursor registration process"""
logging.info(f"{Fore.CYAN} {translator.get('github_register.starting_automation')}{Style.RESET_ALL}")
# Display features and warnings
display_features_and_warnings(translator)
# Get user confirmation
if not get_user_confirmation(translator):
return
# Start registration process
registration = GitHubCursorRegistration(translator)
success = registration.start_registration()
# Display final message
if success:
print(f"\n{Fore.GREEN}{EMOJI['DONE']} {translator.get('github_register.completed_successfully')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_username')}: {registration.github_username}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.github_password')}: {registration.github_password}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.email')}: {registration.email_address}{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('github_register.credentials_saved')}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{EMOJI['ERROR']} {translator.get('github_register.registration_encountered_issues')}{Style.RESET_ALL}")
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('github_register.check_browser_windows_for_manual_intervention_or_try_again_later')}{Style.RESET_ALL}")
# Wait for user acknowledgment
if translator:
input(f"\n{EMOJI['INFO']} {translator.get('register.press_enter')}...")
else:
input(f"\n{EMOJI['INFO']} Press Enter to continue...")
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View File

@@ -20,7 +20,17 @@
"totally_reset": "Нулирайте изцяло Курсор",
"outdate": "Изтекъл срок",
"temp_github_register": "Временно регистриране с GitHub",
"coming_soon": "Очаквайте скоро"
"coming_soon": "Очаквайте скоро",
"fixed_soon": "Ще бъде поправено скоро",
"contribute": "Принос към проекта",
"config": "Покажи конфигурацията",
"delete_google_account": "Изтрий Google акаунта на Cursor",
"continue_prompt": "Продължи? (y/N): ",
"operation_cancelled_by_user": "Операцията е отменена от потребителя",
"exiting": "Излизане ......",
"bypass_version_check": "Пропусни проверката на версията на Cursor",
"check_user_authorized": "Провери оторизацията на потребителя",
"select_chrome_profile": "Избери Chrome профил"
},
"languages": {
"en": "English",

View File

@@ -22,7 +22,16 @@
"temp_github_register": "Temporäre GitHub-Registrierung",
"admin_required": "Ausführen als ausführbare Datei, Administratorrechte erforderlich.",
"admin_required_continue": "Mit der aktuellen Version fortfahren...",
"coming_soon": "Bald verfügbar"
"coming_soon": "Bald verfügbar",
"fixed_soon": "Bald Behoben",
"contribute": "Zum Projekt Beitragen",
"config": "Konfiguration Anzeigen",
"delete_google_account": "Cursor Google-Konto Löschen",
"continue_prompt": "Fortfahren? (y/N): ",
"operation_cancelled_by_user": "Vorgang vom Benutzer abgebrochen",
"exiting": "Wird beendet ……",
"bypass_version_check": "Cursor Versionsprüfung Überspringen",
"check_user_authorized": "Benutzerautorisierung Prüfen"
},
"languages": {
"en": "Englisch",
@@ -34,7 +43,9 @@
"fr": "Französisch",
"pt": "Portugiesisch",
"ru": "Russisch",
"es": "Spanisch"
"es": "Spanisch",
"tr": "Türkisch",
"bg": "Bulgarisch"
},
"quit_cursor": {
"start": "Beginne Cursor zu Beenden",
@@ -101,7 +112,14 @@
"package_not_found": "Package.json Nicht Gefunden: {path}",
"check_version_failed": "Versionsüberprüfung Fehlgeschlagen: {error}",
"stack_trace": "Stack Trace",
"version_too_low": "Cursor-Version Zu Niedrig: {version} < 0.45.0"
"version_too_low": "Cursor-Version Zu Niedrig: {version} < 0.45.0",
"no_write_permission": "Keine Schreibberechtigung: {path}",
"path_not_found": "Pfad Nicht Gefunden: {path}",
"modify_file_failed": "Datei Ändern Fehlgeschlagen: {error}",
"windows_machine_id_updated": "Windows Maschinen-ID Erfolgreich Aktualisiert",
"update_windows_machine_id_failed": "Windows Maschinen-ID Aktualisierung Fehlgeschlagen: {error}",
"update_windows_machine_guid_failed": "Windows Maschinen-GUID Aktualisierung Fehlgeschlagen: {error}",
"file_not_found": "Datei Nicht Gefunden: {path}"
},
"register": {
"title": "Cursor Registrierungstool",

View File

@@ -31,7 +31,8 @@
"operation_cancelled_by_user": "Operation cancelled by user",
"exiting": "Exiting ……",
"bypass_version_check": "Bypass Cursor Version Check",
"check_user_authorized": "Check User Authorized"
"check_user_authorized": "Check User Authorized",
"bypass_token_limit": "Bypass Token Limit"
},
"languages": {
"en": "English",
@@ -198,7 +199,15 @@
"setting_on_password": "Setting Password",
"getting_code": "Getting Verification Code, Will Try in 60s",
"human_verify_error": "Can't verify the user is human. Retrying...",
"max_retries_reached": "Maximum retry attempts reached. Registration failed."
"max_retries_reached": "Maximum retry attempts reached. Registration failed.",
"browser_path_invalid": "{browser} path is invalid, using default path",
"using_browser": "Using {browser} browser: {path}",
"using_browser_profile": "Using {browser} profile from: {user_data_dir}",
"make_sure_browser_is_properly_installed": "Make sure {browser} is properly installed",
"try_install_browser": "Try installing the browser with your package manager",
"tracking_processes": "Tracking {count} {browser} processes",
"no_new_processes_detected": "No new {browser} processes detected to track",
"could_not_track_processes": "Could not track {browser} processes: {error}"
},
"auth": {
"title": "Cursor Auth Manager",
@@ -563,7 +572,10 @@
"backup_failed": "Failed to backup config: {error}",
"force_update_failed": "Force update config failed: {error}",
"config_force_update_disabled": "Config file force update disabled , skipping forced update",
"config_force_update_enabled": "Config file force update enabled , performing forced update"
"config_force_update_enabled": "Config file force update enabled , performing forced update",
"documents_path_not_found": "Documents path not found, using current directory",
"config_dir_created": "Config directory created: {path}",
"using_temp_dir": "Using temporary directory due to error: {path} (Error: {error})"
},
"oauth": {
"authentication_button_not_found": "Authentication button not found",
@@ -622,19 +634,27 @@
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
"browser_failed_to_start": "Browser failed to start: {error}",
"browser_failed": "Browser failed to start: {error}",
"browser_failed_to_start_fallback": "Browser failed to start: {error}"
"browser_failed_to_start_fallback": "Browser failed to start: {error}",
"user_data_dir_not_found": "{browser} user data directory not found at {path}, will try Chrome instead",
"error_getting_user_data_directory": "Error getting user data directory: {error}",
"warning_browser_close": "Warning: This will close all running {browser} processes",
"killing_browser_processes": "Killing {browser} processes...",
"profile_selection_error": "Error during profile selection: {error}",
"using_configured_browser_path": "Using configured {browser} path: {path}",
"browser_not_found_trying_chrome": "Could not find {browser}, trying Chrome instead",
"found_chrome_at": "Found Chrome at: {path}",
"found_browser_user_data_dir": "Found {browser} user data directory: {path}"
},
"chrome_profile": {
"title": "Chrome Profile Selection",
"select_profile": "Select a Chrome profile to use:",
"profile_list": "Available profiles:",
"default_profile": "Default Profile",
"browser_profile": {
"title": "Browser Profile Selection",
"select_profile": "Select {browser} profile to use:",
"profile_list": "Available {browser} profiles:",
"default_profile": "Default profile",
"profile": "Profile {number}",
"no_profiles": "No Chrome profiles found",
"error_loading": "Error loading Chrome profiles: {error}",
"no_profiles": "No {browser} profiles found",
"error_loading": "Error loading {browser} profiles: {error}",
"profile_selected": "Selected profile: {profile}",
"invalid_selection": "Invalid selection. Please try again",
"warning_chrome_close": "Warning: This will close all running Chrome processes"
"invalid_selection": "Invalid selection. Please try again."
},
"account_delete": {
"title": "Cursor Google Account Deletion Tool",
@@ -727,5 +747,22 @@
"checking_usage_information": "Checking usage information...",
"check_usage_response": "Check usage response: {response}",
"usage_response": "Usage response: {response}"
},
"bypass_token_limit": {
"title": "Bypass Token Limit Tool",
"description": "This tool modifies the workbench.desktop.main.js file to bypass the token limit",
"press_enter": "Press Enter to continue..."
},
"token": {
"refreshing": "Refreshing token...",
"refresh_success": "Token refreshed successfully! Valid for {days} days (expires: {expire})",
"no_access_token": "No access token in response",
"refresh_failed": "Token refresh failed: {error}",
"invalid_response": "Invalid JSON response from refresh server",
"server_error": "Refresh server error: HTTP {status}",
"request_timeout": "Request to refresh server timed out",
"connection_error": "Connection error to refresh server",
"unexpected_error": "Unexpected error during token refresh: {error}",
"extraction_error": "Error extracting token: {error}"
}
}

View File

@@ -22,7 +22,15 @@
"admin_required": "Ejecutando como ejecutable, se requieren privilegios de administrador.",
"admin_required_continue": "Continuando sin privilegios de administrador.",
"coming_soon": "Próximamente",
"fixed_soon": "Arreglado Pronto"
"fixed_soon": "Arreglado Pronto",
"contribute": "Contribuir al Proyecto",
"config": "Mostrar Configuración",
"delete_google_account": "Eliminar Cuenta Google de Cursor",
"continue_prompt": "¿Continuar? (y/N): ",
"operation_cancelled_by_user": "Operación cancelada por el usuario",
"exiting": "Saliendo ……",
"bypass_version_check": "Omitir Verificación de Versión de Cursor",
"check_user_authorized": "Verificar Usuario Autorizado"
},
"languages": {
"en": "Inglés",
@@ -103,8 +111,14 @@
"package_not_found": "Package.json No Encontrado: {path}",
"check_version_failed": "Falló la Verificación de Versión: {error}",
"stack_trace": "Traza de la Pila",
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0"
"version_too_low": "Versión de Cursor Muy Baja: {version} < 0.45.0",
"no_write_permission": "Sin Permiso de Escritura: {path}",
"path_not_found": "Ruta No Encontrada: {path}",
"modify_file_failed": "Falló la Modificación del Archivo: {error}",
"windows_machine_id_updated": "ID de Máquina Windows Actualizado Exitosamente",
"update_windows_machine_id_failed": "Falló la Actualización del ID de Máquina Windows: {error}",
"update_windows_machine_guid_failed": "Falló la Actualización del GUID de Máquina Windows: {error}",
"file_not_found": "Archivo No Encontrado: {path}"
},
"register": {
"title": "Herramienta de Registro de Cursor",
@@ -452,4 +466,4 @@
"invalid_selection": "Selección inválida. Por favor, intente de nuevo",
"warning_chrome_close": "Advertencia: Esto cerrará todos los procesos de Chrome en ejecución"
}
}
}

View File

@@ -19,7 +19,16 @@
"totally_reset": "Réinitialisation Complète de Cursor",
"outdate": "Obsolete",
"temp_github_register": "Inscription GitHub temporaire",
"coming_soon": "Bientôt"
"coming_soon": "Bientôt",
"fixed_soon": "Bientôt Corrigé",
"contribute": "Contribuer au Projet",
"config": "Afficher la Configuration",
"delete_google_account": "Supprimer le Compte Google Cursor",
"continue_prompt": "Continuer ? (y/N) : ",
"operation_cancelled_by_user": "Opération annulée par l'utilisateur",
"exiting": "Fermeture ……",
"bypass_version_check": "Ignorer la Vérification de Version de Cursor",
"check_user_authorized": "Vérifier l'Autorisation de l'Utilisateur"
},
"languages": {
"en": "Anglais",
@@ -31,7 +40,9 @@
"fr": "Français",
"pt": "Portugais",
"ru": "Russe",
"es": "Espagnol"
"es": "Espagnol",
"tr": "Turc",
"bg": "Bulgare"
},
"quit_cursor": {
"start": "Début de la Fermeture de Cursor",
@@ -98,7 +109,14 @@
"package_not_found": "Package.json Non Trouvé : {path}",
"check_version_failed": "Échec de la Vérification de la Version : {error}",
"stack_trace": "Trace de la Pile",
"version_too_low": "Version de Cursor Trop Basse : {version} < 0.45.0"
"version_too_low": "Version de Cursor Trop Basse : {version} < 0.45.0",
"no_write_permission": "Pas de Permission d'Écriture : {path}",
"path_not_found": "Chemin Non Trouvé : {path}",
"modify_file_failed": "Échec de la Modification du Fichier : {error}",
"windows_machine_id_updated": "ID de la Machine Windows Mis à Jour avec Succès",
"update_windows_machine_id_failed": "Échec de la Mise à Jour de l'ID de la Machine Windows : {error}",
"update_windows_machine_guid_failed": "Échec de la Mise à Jour du GUID de la Machine Windows : {error}",
"file_not_found": "Fichier Non Trouvé : {path}"
},
"register": {
"title": "Outil d'Enregistrement de Cursor",

View File

@@ -20,7 +20,16 @@
"totally_reset": "Cursor volledig resetten",
"outdate": "Verouderd",
"temp_github_register": "Tijdelijke GitHub-registratie",
"coming_soon": "Binnenkort"
"coming_soon": "Binnenkort",
"fixed_soon": "Binnenkort Opgelost",
"contribute": "Bijdragen aan het Project",
"config": "Configuratie Weergeven",
"delete_google_account": "Cursor Google Account Verwijderen",
"continue_prompt": "Doorgaan? (y/N): ",
"operation_cancelled_by_user": "Operatie geannuleerd door gebruiker",
"exiting": "Afsluiten ……",
"bypass_version_check": "Cursor Versiecontrole Overslaan",
"check_user_authorized": "Gebruikersautorisatie Controleren"
},
"languages": {
"en": "Engels",
@@ -99,7 +108,14 @@
"package_not_found": "Package.json niet gevonden: {path}",
"check_version_failed": "Versiecontrole mislukt: {error}",
"stack_trace": "Stack Trace",
"version_too_low": "Cursor-versie te laag: {version} < 0.45.0"
"version_too_low": "Cursor-versie te laag: {version} < 0.45.0",
"no_write_permission": "Geen schrijfrechten: {path}",
"path_not_found": "Pad niet gevonden: {path}",
"modify_file_failed": "Bestand wijzigen mislukt: {error}",
"windows_machine_id_updated": "Windows Machine-ID succesvol bijgewerkt",
"update_windows_machine_id_failed": "Windows Machine-ID bijwerken mislukt: {error}",
"update_windows_machine_guid_failed": "Windows Machine GUID bijwerken mislukt: {error}",
"file_not_found": "Bestand niet gevonden: {path}"
},
"register": {
"title": "Cursor Registratietool",
@@ -388,4 +404,4 @@
"invalid_selection": "Ongeldige selectie. Probeer het opnieuw",
"warning_chrome_close": "Waarschuwing: Dit zal alle actieve Chrome processen sluiten"
}
}
}

View File

@@ -19,7 +19,16 @@
"totally_reset": "Redefinir Cursor Completamente",
"outdate": "Obsoleto",
"temp_github_register": "Registro temporário do GitHub",
"coming_soon": "Em breve"
"coming_soon": "Em breve",
"fixed_soon": "Será corrigido em breve",
"contribute": "Contribuir para o Projeto",
"config": "Mostrar Configuração",
"delete_google_account": "Excluir Conta Google do Cursor",
"continue_prompt": "Continuar? (y/N): ",
"operation_cancelled_by_user": "Operação cancelada pelo usuário",
"exiting": "Saindo ......",
"bypass_version_check": "Ignorar Verificação de Versão do Cursor",
"check_user_authorized": "Verificar Autorização do Usuário"
},
"languages": {
"en": "Inglês",

View File

@@ -19,7 +19,17 @@
"totally_reset": "Полностью сбросить Cursor",
"outdate": "Устаревший",
"temp_github_register": "Временная регистрация GitHub",
"coming_soon": "Скоро"
"coming_soon": "Скоро",
"fixed_soon": "Скоро будет исправлено",
"contribute": "Внести вклад в проект",
"config": "Показать конфигурацию",
"delete_google_account": "Удалить Google аккаунт Cursor",
"continue_prompt": "Продолжить? (y/N): ",
"operation_cancelled_by_user": "Операция отменена пользователем",
"exiting": "Выход ......",
"bypass_version_check": "Пропустить проверку версии Cursor",
"check_user_authorized": "Проверить авторизацию пользователя",
"select_chrome_profile": "Выбрать профиль Chrome"
},
"languages": {
"en": "Английский",
@@ -396,4 +406,4 @@
"invalid_selection": "Неверный выбор. Пожалуйста, попробуйте снова",
"warning_chrome_close": "Предупреждение: Это закроет все запущенные процессы Chrome"
}
}
}

View File

@@ -9,6 +9,7 @@
"register_manual": "Cursor'ı Özel E-posta ile Kaydet",
"quit": "Cursor Uygulamasını Kapat",
"select_language": "Dili Değiştir",
"select_chrome_profile": "Chrome Profilini Seç",
"input_choice": "Lütfen seçiminizi girin ({choices})",
"invalid_choice": "Geçersiz seçim. Lütfen {choices} arasından bir sayı girin",
"program_terminated": "Program kullanıcı tarafından sonlandırıldı",
@@ -19,7 +20,18 @@
"totally_reset": "Cursor'ı Tamamen Sıfırla",
"outdate": "güncel değil",
"temp_github_register": "Geçici GitHub Kaydı",
"coming_soon": "Yakında"
"admin_required": "Running as executable, administrator privileges required.",
"admin_required_continue": "Continuing without administrator privileges.",
"coming_soon": "Yakında",
"fixed_soon": "Yakında Düzeltilecek",
"contribute": "Projeye Katkıda Bulun",
"config": "Yapılandırmayı Göster",
"delete_google_account": "Cursor Google Hesabını Sil",
"continue_prompt": "Devam et? (y/N): ",
"operation_cancelled_by_user": "İşlem kullanıcı tarafından iptal edildi",
"exiting": ıkılıyor ......",
"bypass_version_check": "Cursor Sürüm Kontrolünü Atla",
"check_user_authorized": "Kullanıcı Yetkilendirmesini Kontrol Et"
},
"languages": {
"en": "English",

View File

@@ -20,13 +20,26 @@
"totally_reset": "Đặt lại hoàn toàn Cursor",
"outdate": "Quá cũ",
"temp_github_register": "Đăng ký GitHub tạm thời",
"coming_soon": "Sắp ra mắt"
"admin_required": "Đang chạy dưới dạng tệp thực thi, yêu cầu quyền quản trị.",
"admin_required_continue": "Tiếp tục mà không có quyền quản trị.",
"coming_soon": "Sắp ra mắt",
"fixed_soon": "Sẽ Sớm Được Sửa",
"contribute": "Đóng Góp Cho Dự Án",
"config": "Hiển Thị Cấu Hình",
"delete_google_account": "Xóa Tài Khoản Google Cursor",
"continue_prompt": "Tiếp tục? (y/N): ",
"operation_cancelled_by_user": "Thao tác đã bị người dùng hủy",
"exiting": "Đang thoát ……",
"bypass_version_check": "Bỏ qua Kiểm tra Phiên bản Cursor",
"check_user_authorized": "Kiểm tra Quyền Người dùng"
},
"languages": {
"en": "Tiếng Anh",
"zh_cn": "Tiếng Trung Giản Thể",
"zh_tw": "Tiếng Trung Phồn Thể",
"vi": "Tiếng Việt",
"tr": "Tiếng Thổ Nhĩ Kỳ",
"bg": "Tiếng Bulgaria",
"nl": "Tiếng Hà Lan",
"de": "Tiếng Đức",
"fr": "Tiếng Pháp",
@@ -99,7 +112,14 @@
"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"
"version_too_low": "Phiên Bản Cursor Quá Thấp: {version} < 0.45.0",
"no_write_permission": "Không Có Quyền Ghi: {path}",
"path_not_found": "Không Tìm Thấy Đường Dẫn: {path}",
"modify_file_failed": "Sửa Đổi Tệp Thất Bại: {error}",
"windows_machine_id_updated": "Cập Nhật ID Máy Windows Thành Công",
"update_windows_machine_id_failed": "Cập Nhật ID Máy Windows Thất Bại: {error}",
"update_windows_machine_guid_failed": "Cập Nhật GUID Máy Windows Thất Bại: {error}",
"file_not_found": "Không Tìm Thấy Tệp: {path}"
},
"register": {
"title": "Công Cụ Đăng Ký Cursor",
@@ -176,10 +196,12 @@
"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"
"getting_code": "Đang Lấy Mã Xác Minh, Sẽ Thử Trong 60s",
"human_verify_error": "Không thể xác minh người dùng. Đang thử lại...",
"max_retries_reached": "Đã đạt số lần thử tối đa. Đăng ký thất bại."
},
"auth": {
"title": "Trình Quản Lý Xác Thực Cursor",
"title": "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}",
@@ -191,7 +213,7 @@
"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_connection_closed": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
"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ị",
@@ -213,7 +235,7 @@
"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",
"token_saved_to_file": "Token Đã Được 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",
@@ -229,12 +251,12 @@
"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",
"database_connection_closed": "Đã Đóng Kết Nối Cơ Sở Dữ Liệu",
"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 domains",
"visiting_site": "Đang Truy Cập Tên Miền Mail",
"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}",
@@ -261,16 +283,22 @@
"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": "Hết Thời Gian Tải Tên Miền Bị Chặn: {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}",
"domain_blocked": "Tên Miền Bị Chặn: {domain}"
"domains_filtered": "Đã Lọc Tên Miền: {count}",
"trying_to_create_email": "Đang Thử Tạo Email: {email}",
"domain_blocked": "Tên Miền Bị Chặn: {domain}",
"using_chrome_profile": "Đang Sử Dụng Hồ Sơ Chrome từ: {user_data_dir}",
"no_display_found": "Không Tìm Thấy Màn Hình. Đảm Bảo X Server Đang Chạy.",
"try_export_display": "Thử: export DISPLAY=:0",
"extension_load_error": "Lỗi Tải Tiện Ích Mở Rộng: {error}",
"make_sure_chrome_chromium_is_properly_installed": "Đảm Bảo Chrome/Chromium Được Cài Đặt Đúng Cách",
"try_install_chromium": "Thử: sudo apt install chromium-browser"
},
"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_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",
@@ -279,122 +307,425 @@
"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"
"block_file_created": "Đã Tạo Tệp Chặn",
"clearing_update_yml": "Đang Xóa Tệp update.yml",
"update_yml_cleared": "Đã Xóa Tệp update.yml",
"update_yml_not_found": "Không Tìm Thấy Tệp update.yml",
"clear_update_yml_failed": "Xóa Tệp update.yml Thất Bại: {error}",
"unsupported_os": "Hệ Điều Hành Không Được Hỗ Trợ: {system}",
"remove_directory_failed": "Xóa Thư Mục Thất Bại: {error}",
"create_block_file_failed": "Tạo Tệp Chặn Thất Bại: {error}",
"directory_locked": "Thư Mục Bị Khóa: {path}",
"yml_locked": "Tệp update.yml Bị Khóa",
"block_file_locked": "Tệp Chặn Bị Khóa",
"yml_already_locked": "Tệp update.yml Đã Bị Khóa",
"block_file_already_locked": "Tệp Chặn Đã Bị Khóa",
"block_file_locked_error": "Lỗi Khóa Tệp Chặn: {error}",
"yml_locked_error": "Lỗi Khóa Tệp update.yml: {error}",
"block_file_already_locked_error": "Lỗi Tệp Chặn Đã Bị Khóa: {error}",
"yml_already_locked_error": "Lỗi Tệp update.yml Đã Bị Khóa: {error}"
},
"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 STĐộng Khởi Động Lại.",
"up_to_date": "Bạn Đang SDụ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'.",
"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 stđộng khởi động lại.",
"up_to_date": "Bạn đang sdụng phiên bản mới nhất.",
"check_failed": "Kiểm tra cập nhật thất bại: {error}",
"continue_anyway": "Tiếp tục với phiên bản hiện tại...",
"update_confirm": "Bạn có muốn cập nhật lên phiên bản mới nhất không? (Y/n)",
"update_skipped": "Bỏ qua cập nhật.",
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập 'Y' hoặc 'n'.",
"development_version": "Phiên Bản Phát Triển {current} > {latest}",
"changelog_title": "Nhật ký thay đổi"
"changelog_title": "Nhật Ký Thay Đổi",
"rate_limit_exceeded": "Đã vượt quá giới hạn API GitHub. Bỏ qua kiểm tra cập nhật."
},
"totally_reset": {
"title": "Đặt lại hoàn toàn Cursor",
"checking_config": "Đang kiểm tra tệp cấu hình",
"config_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_config": "Đ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, bqua bước sao lưu",
"generating_new_machine_id": "Đang tạo ID máy mới",
"saving_new_config": "Đang lưu cấu hình mới vào JSON",
"success": "Đặt lại Cursor thành công",
"error": "Đặt lại Cursor 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ơ sdliệu đã đóng",
"database_updated_successfully": "Cập nhật cơ sdliệu thành công",
"connected_to_database": "Đã kết nối với cơ sdliệu",
"updating_pair": "Đang cập nhật cặp khóa-gtrị",
"title": "Đặt Lại Hoàn Toàn Cursor",
"checking_config": "Đang Kiểm Tra Tệp Cấu Hình",
"config_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_config": "Đ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, BQua Bước Sao Lưu",
"generating_new_machine_id": "Đang Tạo ID Máy Mới",
"saving_new_config": "Đang Lưu Cấu Hình Mới Vào JSON",
"success": "Đặt Lại Cursor Thành Công",
"error": "Đặt Lại Cursor Thất Bại: {error}",
"press_enter": "Nhấn Enter để Thoát",
"reset_machine_id": "Đặt Lại ID Máy",
"database_connection_closed": "Đã Đóng Kết Nối Cơ SDLiệu",
"database_updated_successfully": "Cập Nhật Cơ SDLiệu Thành Công",
"connected_to_database": "Đã Kết Nối Đến Cơ SDLiệu",
"updating_pair": "Đang Cập Nhật Cặp Khóa-GTrị",
"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": "Kết nối cơ sở dữ liệu thất bại: {error}",
"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}",
"feature_title": "TÍNH NĂNG",
"feature_1": "Xóa hoàn toàn các cài đặt và cấu hình của Cursor AI",
"feature_2": "Xóa tất cả dữ liệu bộ nhớ đệm bao gồm lịch sử và gợi ý AI",
"feature_3": "Đặt lại ID máy để vượt qua kiểm tra dùng thử",
"feature_4": "Tạo định danh máy ngẫu nhiên mới",
"feature_5": "Xóa tiện ích mở rộng và tùy chỉnh",
"feature_1": "Xóa hoàn toàn cài đặt và cấu hình của Cursor AI",
"feature_2": "Xóa tất cả dữ liệu đã lưu trong bộ nhớ cache bao gồm lịch sử AI và lời nhắc",
"feature_3": "Đặt lại ID máy để bỏ qua phát hiện dùng thử",
"feature_4": "Tạo định danh máy mới ngẫu nhiên",
"feature_5": "Xóa tiện ích mở rộng và tùy chọn tùy chỉnh",
"feature_6": "Đặt lại thông tin dùng thử và dữ liệu kích hoạt",
"feature_7": "Quét sâu các tệp ẩn liên quan đến giấy phép và dùng thử",
"feature_8": "Bảo toàn các tệp và ứng dụng không liên quan đến Cursor",
"feature_7": "Quét sâu các tệp giấy phép và dùng thử ẩn",
"feature_8": "Bảo toàn an toàn các tệp và ứng dụng không phải của Cursor",
"feature_9": "Tương thích với Windows, macOS và Linux",
"disclaimer_title": "LƯU Ý",
"disclaimer_1": "Công cụ này sẽ xóa vĩnh viễn tất cả cài đặt,",
"disclaimer_2": "cấu hình và dữ liệu bộ nhớ đệm của Cursor AI. Thao tác này không thể hoàn tác.",
"disclaimer_3": "Tệp mã nguồn của bạn sẽ KHÔNG bị ảnh hưởng, công cụ chỉ nhắm đến",
"disclaimer_4": "các tệp chỉnh sửa Cursor AI và cơ chế kiểm tra dùng thử.",
"disclaimer_title": "TUYÊN BỐ MIỄN TRỪ",
"disclaimer_1": "Công cụ này sẽ xóa vĩnh viễn tất cả cài đặt Cursor AI,",
"disclaimer_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
"disclaimer_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, công cụ được thiết kế",
"disclaimer_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
"disclaimer_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
"disclaimer_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
"disclaimer_7": "Sử dụng ới sự tự chịu trách nhiệm",
"disclaimer_7": "Sử dụng với rủi ro của riêng bạn",
"confirm_title": "Bạn có chắc chắn muốn tiếp tục không?",
"confirm_1": "Hành động này sẽ xóa tất cả cài đặt,",
"confirm_2": "cấu hình và dữ liệu bộ nhớ đệm của Cursor AI. Hành động này không thể hoàn tác.",
"confirm_3": "Tệp mã nguồn của bạn sẽ KHÔNG bị ảnh hưởng, công cụ chỉ nhắm đến",
"confirm_4": "các tệp chỉnh sửa Cursor AI và cơ chế kiểm tra dùng thử.",
"confirm_1": "Hành động này sẽ xóa tất cả cài đặt Cursor AI,",
"confirm_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
"confirm_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, công cụ được thiết kế",
"confirm_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
"confirm_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
"confirm_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
"confirm_7": "Sử dụng ới sự tự chịu trách nhiệm",
"confirm_7": "Sử dụng với rủi ro của riêng bạn",
"invalid_choice": "Vui lòng nhập 'Y' hoặc 'n'",
"skipped_for_safety": "Bỏ qua vì an toàn (không liên quan đến Cursor): {path}",
"deleted": "Đã xóa: {path}",
"error_deleting": "Lỗi khi xóa {path}: {error}",
"skipped_for_safety": "Đã bỏ qua vì an toàn (không liên quan đến Cursor): {path}",
"deleted": "Đã Xóa: {path}",
"error_deleting": "Lỗi xóa {path}: {error}",
"not_found": "Không tìm thấy tệp: {path}",
"resetting_machine_id": "Đang đặt lại ID máy để vượt qua kiểm tra dùng thử...",
"resetting_machine_id": "Đang đặt lại định danh máy để bỏ qua phát hiện dùng thử...",
"created_machine_id": "Đã tạo ID máy mới: {path}",
"error_creating_machine_id": "Lỗi khi tạo tệp ID máy {path}: {error}",
"error_searching": "Lỗi khi tìm kiếm tệp trong {path}: {error}",
"error_creating_machine_id": "Lỗi tạo tệp ID máy {path}: {error}",
"error_searching": "Lỗi tìm kiếm tệp trong {path}: {error}",
"created_extended_trial_info": "Đã tạo thông tin dùng thử mở rộng mới: {path}",
"error_creating_trial_info": "Lỗi khi tạo tệp thông tin dùng thử {path}: {error}",
"resetting_cursor_ai_editor": "Đang đặt lại Cursor AI Editor... Vui lòng chờ.",
"reset_cancelled": "Đã hủy đặt lại. Thoát mà không thực hiện thay đổi nào.",
"windows_machine_id_modification_skipped": "Bỏ qua sửa đổi ID máy Windows: {error}",
"linux_machine_id_modification_skipped": "Bỏ qua sửa đổi machine-id Linux: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "Lưu ý: Đặt lại ID máy hoàn toàn có thể yêu cầu chạy ới quyền quản trị viên",
"error_creating_trial_info": "Lỗi tạo tệp thông tin dùng thử {path}: {error}",
"resetting_cursor_ai_editor": "Đang đặt lại Trình soạn thảo Cursor AI... Vui lòng đợi.",
"reset_cancelled": "Đã hủy đặt lại. Thoát mà không thay đổi .",
"windows_machine_id_modification_skipped": "Đã bỏ qua sửa đổi ID máy Windows: {error}",
"linux_machine_id_modification_skipped": "Đã bỏ qua sửa đổi machine-id Linux: {error}",
"note_complete_machine_id_reset_may_require_running_as_administrator": "Lưu ý: Đặt lại ID máy hoàn toàn có thể yêu cầu chạy với quyền quản trị",
"note_complete_system_machine_id_reset_may_require_sudo_privileges": "Lưu ý: Đặt lại machine-id hệ thống hoàn toàn có thể yêu cầu quyền sudo",
"windows_registry_instructions": "📝 LƯU Ý: Để đặt lại hoàn toàn trên Windows, bạn có thể cần dọn dẹp các mục registry.",
"windows_registry_instructions_2": " Chạy 'regedit' và tìm kiếm khóa chứa 'Cursor' hoặc 'CursorAI' dưới HKEY_CURRENT_USER\\Software\\ và xóa chúng.\n",
"reset_log_1": "Cursor AI đã được đặt lại hoàn toàn và vượt qua kiểm tra dùng thử!",
"reset_log_2": "Vui lòng khởi động lại hệ thống để thay đổi có hiệu lực.",
"reset_log_3": "Bạn cần cài đặt lại Cursor AI và sẽ có kỳ dùng thử mới.",
"reset_log_4": "Để có kết quả tốt nhất, bạn có thể cân nhắc:",
"reset_log_5": "Sử dụng địa chỉ email khác khi đăng ký dùng thử mới",
"reset_log_6": "Nếu có thể, sử dụng VPN để thay đổi địa chỉ IP",
"windows_registry_instructions": "📝 LƯU Ý: Để đặt lại hoàn toàn trên Windows, bạn có thể cần xóa các mục đăng ký.",
"windows_registry_instructions_2": " Chạy 'regedit' và tìm kiếm các khóa chứa 'Cursor' hoặc 'CursorAI' trong HKEY_CURRENT_USER\\Software\\ và xóa chúng.",
"reset_log_1": "Cursor AI đã được đặt lại hoàn toàn và bỏ qua phát hiện dùng thử!",
"reset_log_2": "Vui lòng khởi động lại hệ thống để các thay đổi có hiệu lực.",
"reset_log_3": "Bạn sẽ cần cài đặt lại Cursor AI và bây giờ sẽ có một giai đoạn dùng thử mới.",
"reset_log_4": "Để có kết quả tốt nhất, hãy xem xét:",
"reset_log_5": "Sử dụng một địa chỉ email khác khi đăng ký dùng thử mới",
"reset_log_6": "Nếu có thể, sử dụng VPN để thay đổi địa chỉ IP của bạn",
"reset_log_7": "Xóa cookie và bộ nhớ cache trình duyệt trước khi truy cập trang web Cursor AI",
"reset_log_8": "Nếu vn gặp sự cố, hãy thử cài Cursor AI vào vị trí khác",
"reset_log_9": "Nếu gặp bất kỳ vấn đề nào, hãy truy cập Github Issue Tracker và tạo issue tại https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "Đã xảy ra lỗi không mong muốn: {error}",
"report_issue": "Vui lòng báo cáo sự cố này ti Github Issue Tracker: https://github.com/yeongpin/cursor-free-vip/issues",
"keyboard_interrupt": "Quá trình bị người dùng hủy. Đang thoát...",
"return_to_main_menu": "Trở về menu chính...",
"process_interrupted": "Quá trình bị gián đoạn. Đang thoát...",
"reset_log_8": "Nếu vn đề vẫn còn, hãy thử cài đặt Cursor AI ở một vị trí khác",
"reset_log_9": "Nếu bạn gặp bất kỳ vấn đề nào, hãy truy cập Github Issue Tracker và tạo một vấn đề tại https://github.com/yeongpin/cursor-free-vip/issues",
"unexpected_error": "Đã xảy ra lỗi không mong đợi: {error}",
"report_issue": "Vui lòng báo cáo vấn đề này ti Github Issue Tracker tại https://github.com/yeongpin/cursor-free-vip/issues",
"keyboard_interrupt": "Quá trình bị người dùng ngắt. Đang thoát...",
"return_to_main_menu": "Đang trở về menu chính...",
"process_interrupted": "Quá trình bị ngắt. Đang thoát...",
"press_enter_to_return_to_main_menu": "Nhấn Enter để trở về menu chính...",
"removing_known": "Đang xóa các tệp thử nghiệm/giấy phép đã biết",
"performing_deep_scan": "Đang quét sâu để tìm thêm tệp thử nghiệm/giấy phép",
"found_additional_potential_license_trial_files": "Đã tìm thấy {count} tệp thử nghiệm/giấy phép tiềm năng bổ sung",
"checking_for_electron_localstorage_files": "Đang kiểm tra các tệp Electron localStorage",
"no_additional_license_trial_files_found_in_deep_scan": "Không tìm thấy thêm tệp thử nghiệm/giấy phép nào trong quá trình quét sâu",
"removing_electron_localstorage_files": "Đang xóa các tệp Electron localStorage",
"electron_localstorage_files_removed": "Đã xóa các tệp Electron localStorage",
"electron_localstorage_files_removal_error": "Lỗi khi xóa các tệp Electron localStorage: {error}",
"removing_electron_localstorage_files_completed": "Đã hoàn tất việc xóa các tệp Electron localStorage"
"removing_known": "Đang xóa các tệp dùng thử/giấy phép đã biết",
"performing_deep_scan": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
"found_additional_potential_license_trial_files": "Đã tìm thấy {count} tệp giấy phép/dùng thử tiềm năng bổ sung",
"checking_for_electron_localstorage_files": "Đang kiểm tra các tệp localStorage của Electron",
"no_additional_license_trial_files_found_in_deep_scan": "Không tìm thấy tệp giấy phép/dùng thử bổ sung trong quét sâu",
"removing_electron_localstorage_files": "Đang xóa các tệp localStorage của Electron",
"electron_localstorage_files_removed": "Đã xóa các tệp localStorage của Electron",
"electron_localstorage_files_removal_error": "Lỗi xóa các tệp localStorage của Electron: {error}",
"electron_localstorage_files_removal_completed": "Hoàn tất xóa các tệp localStorage của Electron",
"warning_title": "CẢNH BÁO",
"warning_1": "Hành động này sẽ xóa tất cả cài đặt Cursor AI,",
"warning_2": "cấu hình và dữ liệu đã lưu trong bộ nhớ cache. Hành động này không thể hoàn tác.",
"warning_3": "Các tệp mã của bạn sẽ KHÔNG bị ảnh hưởng, và công cụ được thiết kế",
"warning_4": "chỉ nhắm vào các tệp trình soạn thảo Cursor AI và cơ chế phát hiện dùng thử.",
"warning_5": "Các ứng dụng khác trên hệ thống của bạn sẽ không bị ảnh hưởng.",
"warning_6": "Bạn sẽ cần thiết lập lại Cursor AI sau khi chạy công cụ này.",
"warning_7": "Sử dụng với rủi ro của riêng bạn",
"removed": "Đã Xóa: {path}",
"failed_to_reset_machine_guid": "Không thể đặt lại GUID máy",
"failed_to_remove": "Không thể xóa: {path}",
"failed_to_delete_file": "Không thể xóa tệp: {path}",
"failed_to_delete_directory": "Không thể xóa thư mục: {path}",
"failed_to_delete_file_or_directory": "Không thể xóa tệp hoặc thư mục: {path}",
"deep_scanning": "Đang thực hiện quét sâu để tìm thêm tệp dùng thử/giấy phép",
"resetting_cursor": "Đang đặt lại Trình soạn thảo Cursor AI... Vui lòng đợi.",
"completed_in": "Hoàn thành trong {time} giây",
"cursor_reset_completed": "Trình soạn thảo Cursor AI đã được đặt lại hoàn toàn và bỏ qua phát hiện dùng thử!",
"cursor_reset_failed": "Đặt lại Trình soạn thảo Cursor AI thất bại: {error}",
"cursor_reset_cancelled": "Đã hủy đặt lại Trình soạn thảo Cursor AI. Thoát mà không thay đổi gì.",
"operation_cancelled": "Đã hủy thao tác. Thoát mà không thay đổi gì.",
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
"already_on_settings": "Đã ở trang cài đặt",
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục"
},
"github_register": {
"title": "Tự Động Hóa Đăng Ký GitHub + Cursor AI",
"features_header": "Tính Năng",
"feature1": "Tạo email tạm thời sử dụng 1secmail.",
"feature2": "Đăng ký tài khoản GitHub mới với thông tin ngẫu nhiên.",
"feature3": "Tự động xác minh email GitHub.",
"feature4": "Đăng nhập vào Cursor AI sử dụng xác thực GitHub.",
"feature5": "Đặt lại ID máy để bỏ qua phát hiện dùng thử.",
"feature6": "Lưu tất cả thông tin đăng nhập vào tệp.",
"warnings_header": "Cảnh Báo",
"warning1": "Script này tự động hóa việc tạo tài khoản, có thể vi phạm điều khoản dịch vụ của GitHub/Cursor.",
"warning2": "Yêu cầu truy cập internet và quyền quản trị.",
"warning3": "CAPTCHA hoặc xác minh bổ sung có thể làm gián đoạn tự động hóa.",
"warning4": "Sử dụng có trách nhiệm và tự chịu rủi ro.",
"confirm": "Bạn có chắc chắn muốn tiếp tục không?",
"invalid_choice": "Lựa chọn không hợp lệ. Vui lòng nhập 'yes' hoặc 'no'",
"cancelled": "Đã hủy thao tác",
"program_terminated": "Chương trình bị người dùng chấm dứt",
"starting_automation": "Bắt đầu tự động hóa...",
"github_username": "Tên Người Dùng GitHub",
"github_password": "Mật Khẩu GitHub",
"email_address": "Địa Chỉ Email",
"credentials_saved": "Các thông tin đăng nhập này đã được lưu vào github_cursor_accounts.txt",
"completed_successfully": "Đăng ký GitHub + Cursor hoàn tất thành công!",
"registration_encountered_issues": "Đăng ký GitHub + Cursor gặp vấn đề.",
"check_browser_windows_for_manual_intervention_or_try_again_later": "Kiểm tra cửa sổ trình duyệt để can thiệp thủ công hoặc thử lại sau."
},
"account_info": {
"subscription": "Gói Đăng Ký",
"trial_remaining": "Thời Gian Dùng Thử Pro Còn Lại",
"days": "ngày",
"subscription_not_found": "Không tìm thấy thông tin đăng ký",
"email_not_found": "Không tìm thấy email",
"failed_to_get_account": "Không thể lấy thông tin tài khoản",
"config_not_found": "Không tìm thấy cấu hình.",
"failed_to_get_usage": "Không thể lấy thông tin sử dụng",
"failed_to_get_subscription": "Không thể lấy thông tin đăng ký",
"failed_to_get_email": "Không thể lấy địa chỉ email",
"failed_to_get_token": "Không thể lấy token",
"failed_to_get_account_info": "Không thể lấy thông tin tài khoản",
"title": "Thông Tin Tài Khoản",
"email": "Email",
"token": "Token",
"usage": "Sử Dụng",
"subscription_type": "Loại Đăng Ký",
"remaining_trial": "Thời Gian Dùng Thử Còn Lại",
"days_remaining": "Số Ngày Còn Lại",
"premium": "Premium",
"pro": "Pro",
"pro_trial": "Dùng Thử Pro",
"team": "Team",
"enterprise": "Enterprise",
"free": "Miễn Phí",
"active": "Đang Hoạt Động",
"inactive": "Không Hoạt Động",
"premium_usage": "Sử Dụng Premium",
"basic_usage": "Sử Dụng Cơ Bản",
"usage_not_found": "Không tìm thấy thông tin sử dụng",
"lifetime_access_enabled": "Đã Bật Truy Cập Trọn Đời",
"token_not_found": "Không tìm thấy token"
},
"config": {
"config_not_available": "Không có sẵn cấu hình",
"configuration": "Cấu Hình",
"enabled": "Đã Bật",
"disabled": "Đã Tắt",
"config_directory": "Thư Mục Cấu Hình",
"neither_cursor_nor_cursor_directory_found": "Không tìm thấy Cursor hoặc thư mục Cursor trong {config_base}",
"please_make_sure_cursor_is_installed_and_has_been_run_at_least_once": "Vui lòng đảm bảo Cursor đã được cài đặt và đã chạy ít nhất một lần",
"storage_directory_not_found": "Không tìm thấy thư mục lưu trữ: {storage_dir}",
"storage_file_found": "Đã tìm thấy tệp lưu trữ: {storage_path}",
"file_size": "Kích thước tệp: {size} bytes",
"file_permissions": "Quyền tệp: {permissions}",
"file_owner": "Chủ sở hữu tệp: {owner}",
"file_group": "Nhóm tệp: {group}",
"error_getting_file_stats": "Lỗi lấy thông tin tệp: {error}",
"permission_denied": "Từ chối quyền: {storage_path}",
"try_running": "Thử chạy: {command}",
"and": "Và",
"storage_file_is_empty": "Tệp lưu trữ trống: {storage_path}",
"the_file_might_be_corrupted_please_reinstall_cursor": "Tệp có thể bị hỏng, vui lòng cài đặt lại Cursor",
"storage_file_not_found": "Không tìm thấy tệp lưu trữ: {storage_path}",
"error_checking_linux_paths": "Lỗi kiểm tra đường dẫn Linux: {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",
"config_created": "Đã tạo cấu hình: {config_file}",
"config_setup_error": "Lỗi thiết lập cấu hình: {error}",
"storage_file_is_valid_and_contains_data": "Tệp lưu trữ hợp lệ và chứa dữ liệu",
"error_reading_storage_file": "Lỗi đọc tệp lưu trữ: {error}",
"also_checked": "Cũng đã kiểm tra {path}",
"backup_created": "Đã tạo bản sao lưu: {path}",
"config_removed": "Đã xóa tệp cấu hình để cập nhật bắt buộc",
"backup_failed": "Sao lưu cấu hình thất bại: {error}",
"force_update_failed": "Cập nhật bắt buộc cấu hình thất bại: {error}",
"config_force_update_disabled": "Đã tắt cập nhật bắt buộc tệp cấu hình, bỏ qua cập nhật bắt buộc",
"config_force_update_enabled": "Đã bật cập nhật bắt buộc tệp cấu hình, thực hiện cập nhật bắt buộc"
},
"oauth": {
"authentication_button_not_found": "Không tìm thấy nút xác thực",
"authentication_failed": "Xác thực thất bại: {error}",
"found_cookies": "Đã tìm thấy {count} cookie",
"token_extraction_error": "Lỗi trích xuất token: {error}",
"authentication_successful": "Xác thực thành công - Email: {email}",
"missing_authentication_data": "Thiếu dữ liệu xác thực: {data}",
"failed_to_delete_account": "Không thể xóa tài khoản: {error}",
"invalid_authentication_type": "Loại xác thực không hợp lệ",
"auth_update_success": "Cập nhật xác thực thành công",
"browser_closed": "Đã đóng trình duyệt",
"auth_update_failed": "Cập nhật xác thực thất bại",
"google_start": "Bắt đầu Google",
"github_start": "Bắt đầu Github",
"usage_count": "Số lần sử dụng: {usage}",
"account_has_reached_maximum_usage": "Tài khoản đã đạt số lần sử dụng tối đa, {deleting}",
"starting_new_authentication_process": "Bắt đầu quá trình xác thực mới...",
"failed_to_delete_expired_account": "Không thể xóa tài khoản hết hạn",
"could_not_check_usage_count": "Không thể kiểm tra số lần sử dụng: {error}",
"found_email": "Đã tìm thấy email: {email}",
"could_not_find_email": "Không thể tìm thấy email: {error}",
"could_not_find_usage_count": "Không thể tìm thấy số lần sử dụng: {error}",
"already_on_settings_page": "Đã ở trang cài đặt!",
"failed_to_extract_auth_info": "Không thể trích xuất thông tin xác thực: {error}",
"no_chrome_profiles_found": "Không tìm thấy hồ sơ Chrome, sử dụng Mặc định",
"found_default_chrome_profile": "Đã tìm thấy hồ sơ Chrome Mặc định",
"using_first_available_chrome_profile": "Sử dụng hồ sơ Chrome khả dụng đầu tiên: {profile}",
"error_finding_chrome_profile": "Lỗi tìm hồ sơ Chrome, sử dụng Mặc định: {error}",
"initializing_browser_setup": "Đang khởi tạo thiết lập trình duyệt...",
"detected_platform": "Đã phát hiện nền tảng: {platform}",
"running_as_root_warning": "Chạy với quyền root không được khuyến nghị cho tự động hóa trình duyệt",
"consider_running_without_sudo": "Hãy xem xét chạy script mà không cần sudo",
"no_compatible_browser_found": "Không tìm thấy trình duyệt tương thích. Vui lòng cài đặt Google Chrome hoặc Chromium.",
"supported_browsers": "Trình duyệt được hỗ trợ cho {platform}",
"using_browser_profile": "Đang sử dụng hồ sơ trình duyệt: {profile}",
"starting_browser": "Đang khởi động trình duyệt tại: {path}",
"browser_setup_completed": "Thiết lập trình duyệt hoàn tất thành công",
"browser_setup_failed": "Thiết lập trình duyệt thất bại: {error}",
"try_running_without_sudo_admin": "Thử chạy mà không cần quyền sudo/quản trị",
"redirecting_to_authenticator_cursor_sh": "Đang chuyển hướng đến authenticator.cursor.sh...",
"starting_google_authentication": "Bắt đầu xác thực Google...",
"starting_github_authentication": "Bắt đầu xác thực GitHub...",
"waiting_for_authentication": "Đang chờ xác thực...",
"page_changed_checking_auth": "Trang đã thay đổi, đang kiểm tra xác thực...",
"status_check_error": "Lỗi kiểm tra trạng thái: {error}",
"authentication_timeout": "Hết thời gian xác thực",
"account_is_still_valid": "Tài khoản vẫn còn hợp lệ (Sử dụng: {usage})",
"starting_re_authentication_process": "Bắt đầu quá trình xác thực lại...",
"starting_new_google_authentication": "Bắt đầu xác thực Google mới...",
"failed_to_delete_account_or_re_authenticate": "Không thể xóa tài khoản hoặc xác thực lại: {error}",
"navigating_to_authentication_page": "Đang điều hướng đến trang xác thực...",
"please_select_your_google_account_to_continue": "Vui lòng chọn tài khoản Google của bạn để tiếp tục...",
"found_browser_data_directory": "Đã tìm thấy thư mục dữ liệu trình duyệt: {path}",
"authentication_successful_getting_account_info": "Xác thực thành công, đang lấy thông tin tài khoản...",
"warning_could_not_kill_existing_browser_processes": "Cảnh báo: Không thể kết thúc các tiến trình trình duyệt hiện có: {error}",
"browser_failed_to_start": "Trình duyệt không thể khởi động: {error}",
"browser_failed": "Trình duyệt không thể khởi động: {error}",
"browser_failed_to_start_fallback": "Trình duyệt không thể khởi động: {error}"
},
"chrome_profile": {
"title": "Chọn Hồ Sơ Chrome",
"select_profile": "Chọn hồ sơ Chrome để sử dụng:",
"profile_list": "Các hồ sơ có sẵn:",
"select_profile": "Chọn một hồ sơ Chrome để sử dụng:",
"profile_list": "Hồ sơ khả dụng:",
"default_profile": "Hồ Sơ Mặc Định",
"profile": "Hồ Sơ {number}",
"no_profiles": "Không tìm thấy hồ sơ Chrome",
"error_loading": "Lỗi khi tải hồ sơ Chrome: {error}",
"error_loading": "Lỗi tải hồ sơ Chrome: {error}",
"profile_selected": "Đã chọn hồ sơ: {profile}",
"invalid_selection": "Lựa chọn không hợp lệ. Vui lòng thử lại",
"warning_chrome_close": "Cảnh báo: Điều này sẽ đóng tất cả các tiến trình Chrome đang chạy"
},
"account_delete": {
"title": "Công Cụ Xóa Tài Khoản Google Cursor",
"warning": "CẢNH BÁO: Điều này sẽ xóa vĩnh viễn tài khoản Cursor của bạn. Hành động này không thể hoàn tác.",
"cancelled": "Đã hủy xóa tài khoản.",
"starting_process": "Bắt đầu quá trình xóa tài khoản...",
"google_button_not_found": "Không tìm thấy nút đăng nhập Google",
"logging_in": "Đang đăng nhập bằng Google...",
"waiting_for_auth": "Đang chờ xác thực Google...",
"login_successful": "Đăng nhập thành công",
"unexpected_page": "Trang không mong đợi sau khi đăng nhập: {url}",
"trying_settings": "Đang thử điều hướng đến trang cài đặt...",
"select_google_account": "Vui lòng chọn tài khoản Google của bạn...",
"auth_timeout": "Hết thời gian xác thực, vẫn tiếp tục...",
"navigating_to_settings": "Đang điều hướng đến trang cài đặt...",
"already_on_settings": "Đã ở trang cài đặt",
"login_redirect_failed": "Chuyển hướng đăng nhập thất bại, đang thử điều hướng trực tiếp...",
"advanced_tab_not_found": "Không tìm thấy tab Nâng cao sau nhiều lần thử",
"advanced_tab_retry": "Không tìm thấy tab Nâng cao, lần thử {attempt}/{max_attempts}",
"advanced_tab_error": "Lỗi tìm tab Nâng cao: {error}",
"advanced_tab_clicked": "Đã nhấp vào tab Nâng cao",
"direct_advanced_navigation": "Đang thử điều hướng trực tiếp đến tab nâng cao",
"delete_button_not_found": "Không tìm thấy nút Xóa Tài khoản sau nhiều lần thử",
"delete_button_retry": "Không tìm thấy nút Xóa, lần thử {attempt}/{max_attempts}",
"delete_button_error": "Lỗi tìm nút Xóa: {error}",
"delete_button_clicked": "Đã nhấp vào nút Xóa Tài khoản",
"found_danger_zone": "Đã tìm thấy phần Vùng Nguy hiểm",
"delete_input_not_found": "Không tìm thấy ô nhập xác nhận xóa sau nhiều lần thử",
"delete_input_retry": "Không tìm thấy ô nhập xóa, lần thử {attempt}/{max_attempts}",
"delete_input_error": "Lỗi tìm ô nhập Xóa: {error}",
"delete_input_not_found_continuing": "Không tìm thấy ô nhập xác nhận xóa, đang thử tiếp tục",
"typed_delete": "Đã nhập \"Delete\" vào ô xác nhận",
"confirm_button_not_found": "Không tìm thấy nút Xác nhận sau nhiều lần thử",
"confirm_button_retry": "Không tìm thấy nút Xác nhận, lần thử {attempt}/{max_attempts}",
"confirm_button_error": "Lỗi tìm nút Xác nhận: {error}",
"account_deleted": "Đã xóa tài khoản thành công!",
"error": "Lỗi trong quá trình xóa tài khoản: {error}",
"success": "Tài khoản Cursor của bạn đã được xóa thành công!",
"failed": "Quá trình xóa tài khoản thất bại hoặc đã bị hủy.",
"interrupted": "Quá trình xóa tài khoản bị người dùng ngắt.",
"unexpected_error": "Lỗi không mong đợi: {error}",
"found_email": "Đã tìm thấy email: {email}",
"email_not_found": "Không tìm thấy email: {error}",
"confirm_prompt": "Bạn có chắc chắn muốn tiếp tục không? (y/N): "
},
"bypass": {
"starting": "Bắt đầu bỏ qua phiên bản Cursor...",
"found_product_json": "Đã tìm thấy product.json: {path}",
"no_write_permission": "Không có quyền ghi cho tệp: {path}",
"read_failed": "Không thể đọc product.json: {error}",
"current_version": "Phiên bản hiện tại: {version}",
"backup_created": "Đã tạo bản sao lưu: {path}",
"version_updated": "Đã cập nhật phiên bản từ {old} lên {new}",
"write_failed": "Không thể ghi product.json: {error}",
"no_update_needed": "Không cần cập nhật. Phiên bản hiện tại {version} đã >= 0.46.0",
"bypass_failed": "Bỏ qua phiên bản thất bại: {error}",
"stack_trace": "Dấu vết ngăn xếp",
"localappdata_not_found": "Không tìm thấy biến môi trường LOCALAPPDATA",
"product_json_not_found": "Không tìm thấy product.json trong các đường dẫn Linux thông thường",
"unsupported_os": "Hệ điều hành không được hỗ trợ: {system}",
"file_not_found": "Không tìm thấy tệp: {path}",
"title": "Công Cụ Bỏ Qua Phiên Bản Cursor",
"description": "Công cụ này sửa đổi product.json của Cursor để bỏ qua hạn chế phiên bản",
"menu_option": "Bỏ Qua Kiểm Tra Phiên Bản Cursor"
},
"auth_check": {
"checking_authorization": "Đang kiểm tra quyền...",
"token_source": "Lấy token từ cơ sở dữ liệu hay nhập thủ công? (d/m, mặc định: d)",
"getting_token_from_db": "Đang lấy token từ cơ sở dữ liệu...",
"token_found_in_db": "Đã tìm thấy token trong cơ sở dữ liệu",
"token_not_found_in_db": "Không tìm thấy token trong cơ sở dữ liệu",
"cursor_acc_info_not_found": "Không tìm thấy cursor_acc_info.py",
"error_getting_token_from_db": "Lỗi lấy token từ cơ sở dữ liệu: {error}",
"enter_token": "Nhập token Cursor của bạn: ",
"token_length": "Độ dài token: {length} ký tự",
"usage_response_status": "Trạng thái phản hồi sử dụng: {response}",
"unexpected_status_code": "Mã trạng thái không mong đợi: {code}",
"jwt_token_warning": "Token có vẻ ở định dạng JWT, nhưng kiểm tra API trả về mã trạng thái không mong đợi. Token có thể hợp lệ nhưng truy cập API bị hạn chế.",
"invalid_token": "Token không hợp lệ",
"user_authorized": "Người dùng được ủy quyền",
"user_unauthorized": "Người dùng không được ủy quyền",
"request_timeout": "Yêu cầu hết thời gian",
"connection_error": "Lỗi kết nối",
"check_error": "Lỗi kiểm tra quyền: {error}",
"authorization_successful": "Ủy quyền thành công!",
"authorization_failed": "Ủy quyền thất bại!",
"operation_cancelled": "Thao tác bị người dùng hủy",
"unexpected_error": "Lỗi không mong đợi: {error}",
"error_generating_checksum": "Lỗi tạo checksum: {error}",
"checking_usage_information": "Đang kiểm tra thông tin sử dụng...",
"check_usage_response": "Phản hồi kiểm tra sử dụng: {response}",
"usage_response": "Phản hồi sử dụng: {response}"
}
}

View File

@@ -31,7 +31,8 @@
"operation_cancelled_by_user": "操作被用户取消",
"exiting": "退出中 ……",
"bypass_version_check": "绕过 Cursor 版本检查",
"check_user_authorized": "检查用户授权"
"check_user_authorized": "检查用户授权",
"bypass_token_limit": "绕过 Token 限制"
},
"languages": {
"en": "英语",
@@ -196,7 +197,17 @@
"password_submitted": "密码已提交",
"total_usage": "总使用量: {usage}",
"setting_on_password": "设置密码",
"getting_code": "获取验证码将在60秒内尝试..."
"getting_code": "获取验证码将在60秒内尝试...",
"browser_path_invalid": "{browser} 路径无效,使用默认路径",
"using_browser": "正在使用 {browser} 浏览器: {path}",
"using_browser_profile": "使用 {browser} 配置文件: {user_data_dir}",
"make_sure_browser_is_properly_installed": "确保 {browser} 已正确安装",
"try_install_browser": "尝试使用包管理器安装浏览器",
"tracking_processes": "正在跟踪 {count} 个 {browser} 进程",
"no_new_processes_detected": "未检测到新的 {browser} 进程",
"could_not_track_processes": "无法跟踪 {browser} 进程: {error}",
"human_verify_error": "无法验证用户是人类,正在重试...",
"max_retries_reached": "已达到最大重试次数,注册失败。"
},
"auth": {
"title": "Cursor 认证管理器",
@@ -541,7 +552,10 @@
"backup_failed": "备份失败: {error}",
"force_update_failed": "强制更新配置失败: {error}",
"config_force_update_disabled": "配置文件强制更新已禁用,跳过强制更新",
"config_force_update_enabled": "配置文件强制更新已启用,正在执行强制更新"
"config_force_update_enabled": "配置文件强制更新已启用,正在执行强制更新",
"documents_path_not_found": "找不到文档路径,使用当前目录",
"config_dir_created": "已创建配置目录: {path}",
"using_temp_dir": "由于错误使用临时目录: {path} (错误: {error})"
},
"oauth": {
"authentication_button_not_found": "未找到认证按钮",
@@ -600,19 +614,27 @@
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
"browser_failed_to_start": "浏览器启动失败: {error}",
"browser_failed": "浏览器启动失败: {error}",
"browser_failed_to_start_fallback": "浏览器启动失败: {error}"
"browser_failed_to_start_fallback": "浏览器启动失败: {error}",
"user_data_dir_not_found": "{browser} 用户数据目录未找到:{path},将尝试使用 Chrome",
"error_getting_user_data_directory": "获取用户数据目录出错:{error}",
"warning_browser_close": "警告:这将关闭所有正在运行的 {browser} 进程",
"killing_browser_processes": "正在关闭 {browser} 进程...",
"profile_selection_error": "配置文件选择过程中出错: {error}",
"using_configured_browser_path": "使用配置的 {browser} 路径: {path}",
"browser_not_found_trying_chrome": "未找到 {browser},尝试使用 Chrome 代替",
"found_chrome_at": "找到 Chrome: {path}",
"found_browser_user_data_dir": "找到 {browser} 用户数据目录: {path}"
},
"chrome_profile": {
"title": "Chrome配置文件选择",
"select_profile": "选择要使用的Chrome配置文件:",
"profile_list": "可用配置文件:",
"browser_profile": {
"title": "浏览器配置文件选择",
"select_profile": "选择要使用的{browser}配置文件:",
"profile_list": "可用{browser}配置文件:",
"default_profile": "默认配置文件",
"profile": "配置文件 {number}",
"no_profiles": "未找到Chrome配置文件",
"error_loading": "加载Chrome配置文件时出错:{error}",
"no_profiles": "未找到{browser}配置文件",
"error_loading": "加载{browser}配置文件时出错:{error}",
"profile_selected": "已选择配置文件:{profile}",
"invalid_selection": "选择无效。请重试",
"warning_chrome_close": "警告这将关闭所有正在运行的Chrome进程"
"invalid_selection": "选择无效。请重试"
},
"account_delete": {
"title": "Cursor Google 账号删除工具",
@@ -677,7 +699,7 @@
"title": "Cursor 版本绕过工具",
"description": "此工具修改 Cursor 的 product.json 以绕过版本限制",
"menu_option": "绕过 Cursor 版本检查"
},
},
"auth_check": {
"checking_authorization": "检查授权...",
"token_source": "从数据库获取 token 或手动输入d/m, 默认: d",
@@ -705,5 +727,22 @@
"checking_usage_information": "检查使用情况...",
"check_usage_response": "检查使用情况响应: {response}",
"usage_response": "使用情况响应: {response}"
},
"bypass_token_limit": {
"title": "绕过 Token 限制工具",
"description": "此工具修改 workbench.desktop.main.js 文件以绕过 token 限制",
"press_enter": "按回车键继续..."
},
"token": {
"refreshing": "正在刷新令牌...",
"refresh_success": "令牌刷新成功!有效期 {days} 天(到期时间: {expire}",
"no_access_token": "响应中没有访问令牌",
"refresh_failed": "令牌刷新失败: {error}",
"invalid_response": "刷新服务器返回无效的 JSON 响应",
"server_error": "刷新服务器错误: HTTP {status}",
"request_timeout": "刷新服务器请求超时",
"connection_error": "连接刷新服务器错误",
"unexpected_error": "令牌刷新过程中出现意外错误: {error}",
"extraction_error": "提取令牌时出错: {error}"
}
}
}

View File

@@ -31,7 +31,8 @@
"operation_cancelled_by_user": "操作被使用者取消",
"exiting": "退出中 ……",
"bypass_version_check": "繞過 Cursor 版本檢查",
"check_user_authorized": "檢查用戶授權"
"check_user_authorized": "檢查用戶授權",
"bypass_token_limit": "繞過 Token 限制"
},
"languages": {
"en": "英文",
@@ -148,6 +149,22 @@
"no_turnstile": "未檢測到 Turnstile 驗證",
"turnstile_passed": "驗證通過",
"verification_start": "開始獲取驗證碼",
"verification_timeout": "獲取驗證碼超時",
"verification_not_found": "未找到驗證碼",
"try_get_code": "第 {attempt} 次嘗試獲取驗證碼 | 剩餘時間: {time}秒",
"get_account": "獲取帳戶信息",
"get_token": "獲取 Cursor Session Token",
"token_success": "Token 獲取成功",
"token_attempt": "第 {attempt} 次嘗試未獲取到 Token{time}秒後重試",
"token_max_attempts": "已達到最大嘗試次數({max}),獲取 Token 失敗",
"token_failed": "獲取 Token 失敗: {error}",
"account_error": "獲取帳戶信息失敗: {error}",
"email_error": "獲取郵箱地址失敗",
"setup_error": "郵箱設置出錯: {error}",
"start_getting_verification_code": "開始獲取驗證碼將在60秒內嘗試...",
"get_verification_code_timeout": "獲取驗證碼超時",
"get_verification_code_success": "成功獲取驗證碼",
"try_get_verification_code": "第 {attempt} 次嘗試未獲取到驗證碼,剩餘時間: {remaining_time}秒",
"verification_code_filled": "驗證碼填寫完成",
"login_success_and_jump_to_settings_page": "成功登錄並跳轉到設置頁面",
"detect_login_page": "檢測到登錄頁面,開始登錄...",
@@ -687,5 +704,10 @@
"checking_usage_information": "檢查使用情況...",
"check_usage_response": "檢查使用情況響應: {response}",
"usage_response": "使用情況響應: {response}"
},
"bypass_token_limit": {
"title": "繞過 Token 限制工具",
"description": "此工具修改 workbench.desktop.main.js 文件以繞過 token 限制",
"press_enter": "按回車鍵繼續..."
}
}
}

279
main.py
View File

@@ -110,18 +110,24 @@ class Translator:
threadid = user32.GetWindowThreadProcessId(hwnd, 0)
layout_id = user32.GetKeyboardLayout(threadid) & 0xFFFF
# Map language ID to our language codes
language_map = {
0x0409: 'en', # English
0x0404: 'zh_tw', # Traditional Chinese
0x0804: 'zh_cn', # Simplified Chinese
0x0422: 'vi', # Vietnamese
0x0419: 'ru', # Russian
0x0415: 'tr', # Turkish
0x0402: 'bg', # Bulgarian
}
return language_map.get(layout_id, 'en')
# Map language ID to our language codes using match-case
match layout_id:
case 0x0409:
return 'en' # English
case 0x0404:
return 'zh_tw' # Traditional Chinese
case 0x0804:
return 'zh_cn' # Simplified Chinese
case 0x0422:
return 'vi' # Vietnamese
case 0x0419:
return 'ru' # Russian
case 0x0415:
return 'tr' # Turkish
case 0x0402:
return 'bg' # Bulgarian
case _:
return 'en' # Default to English
except:
return self._detect_unix_language()
@@ -129,59 +135,63 @@ class Translator:
"""Detect language on Unix-like systems (Linux, macOS)"""
try:
# Get the system locale
system_locale = locale.getdefaultlocale()[0]
locale.setlocale(locale.LC_ALL, '')
system_locale = locale.getlocale()[0]
if not system_locale:
return 'en'
system_locale = system_locale.lower()
# Map locale to our language codes
if system_locale.startswith('zh_tw') or system_locale.startswith('zh_hk'):
return 'zh_tw'
elif system_locale.startswith('zh_cn'):
return 'zh_cn'
elif system_locale.startswith('en'):
return 'en'
elif system_locale.startswith('vi'):
return 'vi'
elif system_locale.startswith('nl'):
return 'nl'
elif system_locale.startswith('de'):
return 'de'
elif system_locale.startswith('fr'):
return 'fr'
elif system_locale.startswith('pt'):
return 'pt'
elif system_locale.startswith('ru'):
return 'ru'
elif system_locale.startswith('tr'):
return 'tr'
elif system_locale.startswith('bg'):
return 'bg'
# Try to get language from LANG environment variable as fallback
env_lang = os.getenv('LANG', '').lower()
if 'tw' in env_lang or 'hk' in env_lang:
return 'zh_tw'
elif 'cn' in env_lang:
return 'zh_cn'
elif 'vi' in env_lang:
return 'vi'
elif 'nl' in env_lang:
return 'nl'
elif 'de' in env_lang:
return 'de'
elif 'fr' in env_lang:
return 'fr'
elif 'pt' in env_lang:
return 'pt'
elif 'ru' in env_lang:
return 'ru'
elif 'tr' in env_lang:
return 'tr'
elif 'bg' in env_lang:
return 'bg'
return 'en'
# Map locale to our language codes using match-case
match system_locale:
case s if s.startswith('zh_tw') or s.startswith('zh_hk'):
return 'zh_tw'
case s if s.startswith('zh_cn'):
return 'zh_cn'
case s if s.startswith('en'):
return 'en'
case s if s.startswith('vi'):
return 'vi'
case s if s.startswith('nl'):
return 'nl'
case s if s.startswith('de'):
return 'de'
case s if s.startswith('fr'):
return 'fr'
case s if s.startswith('pt'):
return 'pt'
case s if s.startswith('ru'):
return 'ru'
case s if s.startswith('tr'):
return 'tr'
case s if s.startswith('bg'):
return 'bg'
case _:
# Try to get language from LANG environment variable as fallback
env_lang = os.getenv('LANG', '').lower()
match env_lang:
case s if 'tw' in s or 'hk' in s:
return 'zh_tw'
case s if 'cn' in s:
return 'zh_cn'
case s if 'vi' in s:
return 'vi'
case s if 'nl' in s:
return 'nl'
case s if 'de' in s:
return 'de'
case s if 'fr' in s:
return 'fr'
case s if 'pt' in s:
return 'pt'
case s if 'ru' in s:
return 'ru'
case s if 'tr' in s:
return 'tr'
case s if 'bg' in s:
return 'bg'
case _:
return 'en'
except:
return 'en'
@@ -274,21 +284,15 @@ def print_menu():
menu_items = {
0: f"{Fore.GREEN}0{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.exit')}",
1: f"{Fore.GREEN}1{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.reset')}",
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register')} ({Fore.RED}{translator.get('menu.outdate')}{Style.RESET_ALL})",
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['SUN']} {translator.get('menu.register_google')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['STAR']} {translator.get('menu.register_github')} {EMOJI['ROCKET']} ({Fore.YELLOW}{translator.get('menu.lifetime_access_enabled')}{Style.RESET_ALL})",
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['SUCCESS']} {translator.get('menu.register_manual')}",
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.temp_github_register')}",
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
11: f"{Fore.GREEN}11{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}",
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account', fallback='Delete Cursor Google Account')}",
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}"
2: f"{Fore.GREEN}2{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.quit')}",
3: f"{Fore.GREEN}3{Style.RESET_ALL}. {EMOJI['LANG']} {translator.get('menu.select_language')}",
4: f"{Fore.GREEN}4{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.disable_auto_update')}",
5: f"{Fore.GREEN}5{Style.RESET_ALL}. {EMOJI['RESET']} {translator.get('menu.totally_reset')}",
6: f"{Fore.GREEN}6{Style.RESET_ALL}. {EMOJI['CONTRIBUTE']} {translator.get('menu.contribute')}",
7: f"{Fore.GREEN}7{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
8: f"{Fore.GREEN}8{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
9: f"{Fore.GREEN}9{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}",
10: f"{Fore.GREEN}10{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit', fallback='Bypass Token Limit')}"
}
# Automatically calculate the number of menu items in the left and right columns
@@ -561,86 +565,61 @@ def main():
while True:
try:
choice_num = 16
choice_num = 10
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('menu.input_choice', choices=f'0-{choice_num}')}: {Style.RESET_ALL}")
if choice == "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
return
elif choice == "1":
import reset_machine_manual
reset_machine_manual.run(translator)
print_menu()
elif choice == "2":
import cursor_register
cursor_register.main(translator)
print_menu()
elif choice == "3":
import cursor_register_google
cursor_register_google.main(translator)
print_menu()
elif choice == "4":
import cursor_register_github
cursor_register_github.main(translator)
print_menu()
elif choice == "5":
import cursor_register_manual
cursor_register_manual.main(translator)
print_menu()
elif choice == "6":
import github_cursor_register
print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.coming_soon')}{Style.RESET_ALL}")
# github_cursor_register.main(translator)
print_menu()
elif choice == "7":
import quit_cursor
quit_cursor.quit_cursor(translator)
print_menu()
elif choice == "8":
if select_language():
match choice:
case "0":
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.exit')}...{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
return
case "1":
import reset_machine_manual
reset_machine_manual.run(translator)
print_menu()
case "2":
import quit_cursor
quit_cursor.quit_cursor(translator)
print_menu()
case "3":
if select_language():
print_menu()
continue
case "4":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
case "5":
import totally_reset_cursor
totally_reset_cursor.run(translator)
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
print_menu()
case "6":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
case "7":
from config import print_config
print_config(get_config(), translator)
print_menu()
case "8":
import bypass_version
bypass_version.main(translator)
print_menu()
case "9":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
case "10":
import bypass_token_limit
bypass_token_limit.run(translator)
print_menu()
case _:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()
continue
elif choice == "9":
import disable_auto_update
disable_auto_update.run(translator)
print_menu()
elif choice == "10":
import totally_reset_cursor
totally_reset_cursor.run(translator)
# print(f"{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.fixed_soon')}{Style.RESET_ALL}")
print_menu()
elif choice == "11":
import logo
print(logo.CURSOR_CONTRIBUTORS)
print_menu()
elif choice == "12":
from config import print_config
print_config(get_config(), translator)
print_menu()
elif choice == "13":
from oauth_auth import OAuthHandler
oauth = OAuthHandler(translator)
oauth._select_profile()
print_menu()
elif choice == "14":
import delete_cursor_google
delete_cursor_google.main(translator)
print_menu()
elif choice == "15":
import bypass_version
bypass_version.main(translator)
print_menu()
elif choice == "16":
import check_user_authorized
check_user_authorized.main(translator)
print_menu()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
print_menu()
except KeyboardInterrupt:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('menu.program_terminated')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'' * 50}{Style.RESET_ALL}")
return
except Exception as e:

View File

@@ -1,695 +0,0 @@
from DrissionPage import ChromiumOptions, ChromiumPage
import time
import os
import signal
import random
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
# Add global variable to track our Chrome processes
_chrome_process_ids = []
def cleanup_chrome_processes(translator=None):
"""Clean only Chrome processes launched by this script"""
global _chrome_process_ids
if not _chrome_process_ids:
print("\nNo Chrome processes to clean...")
return
print("\nCleaning Chrome processes launched by this script...")
try:
if os.name == 'nt':
for pid in _chrome_process_ids:
try:
os.system(f'taskkill /F /PID {pid} /T 2>nul')
except:
pass
else:
for pid in _chrome_process_ids:
try:
os.kill(pid, signal.SIGTERM)
except:
pass
_chrome_process_ids = [] # Reset the list after cleanup
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.cleanup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"清理进程时出错: {e}")
def signal_handler(signum, frame):
"""Handle Ctrl+C signal"""
global _translator
if _translator:
print(f"{Fore.CYAN}{_translator.get('register.exit_signal')}{Style.RESET_ALL}")
else:
print("\n接收到退出信号,正在关闭...")
cleanup_chrome_processes(_translator)
os._exit(0)
def simulate_human_input(page, url, config, translator=None):
"""Visit URL"""
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
# First visit blank page
page.get('about:blank')
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# Visit target page
page.get(url)
time.sleep(get_random_wait_time(config, 'page_load_wait'))
def fill_signup_form(page, first_name, last_name, email, config, translator=None):
"""Fill signup form"""
try:
if translator:
print(f"{Fore.CYAN}📧 {translator.get('register.filling_form')}{Style.RESET_ALL}")
else:
print("\n正在填写注册表单...")
# Fill first name
first_name_input = page.ele("@name=first_name")
if first_name_input:
first_name_input.input(first_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# Fill last name
last_name_input = page.ele("@name=last_name")
if last_name_input:
last_name_input.input(last_name)
time.sleep(get_random_wait_time(config, 'input_wait'))
# Fill email
email_input = page.ele("@name=email")
if email_input:
email_input.input(email)
time.sleep(get_random_wait_time(config, 'input_wait'))
# Click submit button
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.form_success')}{Style.RESET_ALL}")
else:
print("Form filled successfully")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.form_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error filling form: {e}")
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):
"""Setup browser driver"""
global _chrome_process_ids
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()
# Set Chrome path
co.set_browser_path(chrome_path)
# Use incognito mode
co.set_argument("--incognito")
if sys.platform == "linux":
# Set random port
co.set_argument("--no-sandbox")
# Set random port
co.auto_port()
# Use headless mode (must be set to False, simulate human operation)
co.headless(False)
try:
# Load extension
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
if os.path.exists(extension_path):
co.set_argument("--allow-extensions-in-incognito")
co.add_extension(extension_path)
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.extension_load_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error loading extension: {e}")
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.starting_browser')}{Style.RESET_ALL}")
else:
print("Starting browser...")
# Record Chrome processes before launching
before_pids = []
try:
import psutil
before_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
except:
pass
# Launch browser
page = ChromiumPage(co)
# Wait a moment for Chrome to fully launch
time.sleep(1)
# Record Chrome processes after launching and find new ones
try:
import psutil
after_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
# Find new Chrome processes
new_pids = [pid for pid in after_pids if pid not in before_pids]
_chrome_process_ids.extend(new_pids)
if _chrome_process_ids:
print(f"Tracking {len(_chrome_process_ids)} Chrome processes")
else:
print(f"{Fore.YELLOW}Warning: No new Chrome processes detected to track{Style.RESET_ALL}")
except Exception as e:
print(f"Warning: Could not track Chrome processes: {e}")
return config, page
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.browser_setup_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error setting up browser: {e}")
raise
def handle_turnstile(page, config, translator=None):
"""Handle Turnstile verification"""
try:
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.handling_turnstile')}{Style.RESET_ALL}")
else:
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
retry_count = 0
while retry_count < max_retries:
retry_count += 1
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.retry_verification', attempt=retry_count)}{Style.RESET_ALL}")
else:
print(f"Attempt {retry_count} of verification...")
try:
# Try to reset turnstile
page.run_js("try { turnstile.reset() } catch(e) { }")
time.sleep(turnstile_time) # from config
# Locate verification box element
challenge_check = (
page.ele("@id=cf-turnstile", timeout=2)
.child()
.shadow_root.ele("tag:iframe")
.ele("tag:body")
.sr("tag:input")
)
if challenge_check:
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.detect_turnstile')}{Style.RESET_ALL}")
else:
print("Detected verification box...")
# from config
time.sleep(random.uniform(min_time, max_time))
challenge_check.click()
time.sleep(turnstile_time) # from config
# check verification result
if check_verification_success(page, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("Verification successful!")
return True
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print(f"Verification attempt failed: {e}")
# Check if verification has been successful
if check_verification_success(page, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
else:
print("Verification successful!")
return True
time.sleep(random.uniform(min_time, max_time))
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("Exceeded maximum retry attempts")
return False
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
else:
print(f"Error in verification process: {e}")
return False
def check_verification_success(page, translator=None):
"""Check if verification is successful"""
try:
# Check if there is a subsequent form element, indicating verification has passed
if (page.ele("@name=password", timeout=0.5) or
page.ele("@name=email", timeout=0.5) or
page.ele("@data-index=0", timeout=0.5) or
page.ele("Account Settings", timeout=0.5)):
return True
# Check if there is an error message
error_messages = [
'xpath://div[contains(text(), "Can\'t verify the user is human")]',
'xpath://div[contains(text(), "Error: 600010")]',
'xpath://div[contains(text(), "Please try again")]'
]
for error_xpath in error_messages:
if page.ele(error_xpath):
return False
return False
except:
return False
def generate_password(length=12):
"""Generate random password"""
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def fill_password(page, password: str, config, translator=None):
"""
Fill password form
"""
try:
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else 'Setting password'}{Style.RESET_ALL}")
# Fill password
password_input = page.ele("@name=password")
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_on_password')}: {password}{Style.RESET_ALL}")
if password_input:
password_input.input(password)
# Click submit button
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
time.sleep(get_random_wait_time(config, 'submit_wait'))
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else 'Password submitted'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'Error setting password: {str(e)}'}{Style.RESET_ALL}")
return False
def handle_verification_code(browser_tab, email_tab, controller, config, translator=None):
"""Handle verification code"""
try:
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
# Check if using manual input verification code
if hasattr(controller, 'get_verification_code') and email_tab is None: # Manual mode
verification_code = controller.get_verification_code()
if verification_code:
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
print(f"{translator.get('register.verification_success')}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# Visit settings page
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab
return False, None
# Automatic verification code logic
elif email_tab:
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'email_check_initial_wait'))
# Use existing email_tab to refresh email
email_tab.refresh_inbox()
time.sleep(get_random_wait_time(config, 'email_refresh_wait'))
# Check if there is a verification code email
if email_tab.check_for_cursor_email():
verification_code = email_tab.get_verification_code()
if verification_code:
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# Visit settings page
if translator:
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab
else:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
else:
print("最后一次验证失败")
return False, None
# Get verification code, set timeout
verification_code = None
max_attempts = 20
retry_interval = get_random_wait_time(config, 'retry_interval') # Use get_random_wait_time
start_time = time.time()
timeout = float(config.get('Timing', 'max_timeout', fallback='160')) # This can be kept unchanged because it is a fixed value
if translator:
print(f"{Fore.CYAN}{translator.get('register.start_getting_verification_code')}{Style.RESET_ALL}")
for attempt in range(max_attempts):
# Check if timeout
if time.time() - start_time > timeout:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_timeout')}{Style.RESET_ALL}")
break
verification_code = controller.get_verification_code()
if verification_code:
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
break
remaining_time = int(timeout - (time.time() - start_time))
if translator:
print(f"{Fore.CYAN}{translator.get('register.try_get_code', attempt=attempt + 1, time=remaining_time)}{Style.RESET_ALL}")
# Refresh email
email_tab.refresh_inbox()
time.sleep(retry_interval) # Use get_random_wait_time
if verification_code:
# Fill verification code in registration page
for i, digit in enumerate(verification_code):
browser_tab.ele(f"@data-index={i}").input(digit)
time.sleep(get_random_wait_time(config, 'verification_code_input'))
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_success_wait'))
# Handle last Turnstile verification
if handle_turnstile(browser_tab, config, translator):
if translator:
print(f"{Fore.GREEN}{translator.get('register.verification_success')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# Visit settings page
if translator:
print(f"{Fore.CYAN}{translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
# Return success directly, let cursor_register.py handle account information acquisition
return True, browser_tab
else:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_failed')}{Style.RESET_ALL}")
return False, None
return False, None
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.verification_error', error=str(e))}{Style.RESET_ALL}")
return False, None
def handle_sign_in(browser_tab, email, password, translator=None):
"""Handle login process"""
try:
# Check if on login page
sign_in_header = browser_tab.ele('xpath://h1[contains(text(), "Sign in")]')
if not sign_in_header:
return True # If not on login page, it means login is successful
print(f"{Fore.CYAN}检测到登录页面,开始登录...{Style.RESET_ALL}")
# Fill email
email_input = browser_tab.ele('@name=email')
if email_input:
email_input.input(email)
time.sleep(1)
# Click Continue
continue_button = browser_tab.ele('xpath://button[contains(@class, "BrandedButton") and text()="Continue"]')
if continue_button:
continue_button.click()
time.sleep(2)
# Handle Turnstile verification
if handle_turnstile(browser_tab, translator):
# Fill password
password_input = browser_tab.ele('@name=password')
if password_input:
password_input.input(password)
time.sleep(1)
# Click Sign in
sign_in_button = browser_tab.ele('xpath://button[@name="intent" and @value="password"]')
if sign_in_button:
sign_in_button.click()
time.sleep(2)
# Handle last Turnstile verification
if handle_turnstile(browser_tab, translator):
print(f"{Fore.GREEN}Login successful!{Style.RESET_ALL}")
time.sleep(3)
return True
print(f"{Fore.RED}Login failed{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}Login process error: {str(e)}{Style.RESET_ALL}")
return False
def main(email=None, password=None, first_name=None, last_name=None, email_tab=None, controller=None, translator=None):
"""Main function, can receive account information, email tab, and translator"""
global _translator
global _chrome_process_ids
_translator = translator # Save to global variable
_chrome_process_ids = [] # Reset the process IDs list
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
page = None
success = False
try:
config, page = setup_driver(translator)
if translator:
print(f"{Fore.CYAN}🚀 {translator.get('register.browser_started')}{Style.RESET_ALL}")
# Visit registration page
url = "https://authenticator.cursor.sh/sign-up"
# Visit page
simulate_human_input(page, url, config, translator)
if translator:
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# If account information is not provided, generate random information
if not all([email, password, first_name, last_name]):
first_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
last_name = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=6)).capitalize()
email = f"{first_name.lower()}{random.randint(100,999)}@example.com"
password = generate_password()
# Save account information
with open('test_accounts.txt', 'a', encoding='utf-8') as f:
f.write(f"\n{'='*50}\n")
f.write(f"Email: {email}\n")
f.write(f"Password: {password}\n")
f.write(f"{'='*50}\n")
# Fill form
if fill_signup_form(page, first_name, last_name, email, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}")
# Handle first Turnstile verification
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
# Fill password
if fill_password(page, password, config, translator):
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
# Handle second Turnstile verification
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
if handle_verification_code(page, email_tab, controller, config, translator):
success = True
return True, page
else:
print(f"\n{Fore.RED}{translator.get('register.verification_code_processing_failed') if translator else 'Verification code processing failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.second_verification_failed') if translator else 'Second verification failed'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED}{translator.get('register.first_verification_failed') if translator else 'First verification failed'}{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"发生错误: {e}")
return False, None
finally:
if page and not success: # Only clean up when failed
try:
page.quit()
except:
pass
cleanup_chrome_processes(translator)
if __name__ == "__main__":
main() # Run without parameters, use randomly generated information

View File

@@ -1,337 +0,0 @@
from DrissionPage import ChromiumPage, ChromiumOptions
import time
import os
import sys
from colorama import Fore, Style, init
import requests
import random
import string
from utils import get_random_wait_time
# Initialize colorama
init()
class NewTempEmail:
def __init__(self, translator=None):
self.translator = translator
self.page = None
self.setup_browser()
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 self._load_local_blocked_domains()
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 self._load_local_blocked_domains()
def _load_local_blocked_domains(self):
"""Load blocked domains from local file as fallback"""
try:
local_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "block_domain.txt")
if os.path.exists(local_path):
with open(local_path, 'r', encoding='utf-8') as f:
domains = [line.strip() for line in f.readlines() if line.strip()]
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.local_blocked_domains_loaded', count=len(domains))}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 已从本地加载 {len(domains)} 个被屏蔽的域名{Style.RESET_ALL}")
return domains
else:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_blocked_domains_not_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 本地被屏蔽域名文件不存在{Style.RESET_ALL}")
return []
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.local_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 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 setup_browser(self):
"""设置浏览器"""
try:
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.starting_browser')}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 正在启动浏览器...{Style.RESET_ALL}")
# 创建浏览器选项
co = ChromiumOptions()
# Only use headless for non-OAuth operations
if not hasattr(self, 'auth_type') or self.auth_type != 'oauth':
co.set_argument("--headless=new")
if sys.platform == "linux":
# Check if DISPLAY is set when not in headless mode
if not co.arguments.get("--headless=new") and not os.environ.get('DISPLAY'):
print(f"{Fore.RED}{self.translator.get('email.no_display_found') if self.translator else 'No display found. Make sure X server is running.'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW} {self.translator.get('email.try_export_display') if self.translator else 'Try: export DISPLAY=:0'}{Style.RESET_ALL}")
return False
co.set_argument("--no-sandbox")
co.set_argument("--disable-dev-shm-usage")
co.set_argument("--disable-gpu")
# If running as root, try to use actual user's Chrome profile
if os.geteuid() == 0:
sudo_user = os.environ.get('SUDO_USER')
if sudo_user:
actual_home = f"/home/{sudo_user}"
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
if os.path.exists(user_data_dir):
print(f"{Fore.CYAN} {self.translator.get('email.using_chrome_profile', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
co.set_argument(f"--user-data-dir={user_data_dir}")
co.auto_port() # 自动设置端口
# 加载 uBlock 插件
try:
extension_path = self.get_extension_block()
co.set_argument("--allow-extensions-in-incognito")
co.add_extension(extension_path)
except Exception as e:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.extension_load_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 加载插件失败: {str(e)}{Style.RESET_ALL}")
self.page = ChromiumPage(co)
return True
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.browser_start_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
if sys.platform == "linux":
print(f"{Fore.YELLOW} {self.translator.get('email.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
print(f"{Fore.YELLOW} {self.translator.get('email.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
return False
def create_email(self):
"""create temporary email"""
try:
if self.translator:
print(f"{Fore.CYAN} {self.translator.get('email.visiting_site')}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN} 正在访问 smailpro.com...{Style.RESET_ALL}")
# load blocked domains list
self.blocked_domains = self.get_blocked_domains()
# visit website
self.page.get("https://smailpro.com/")
time.sleep(2)
# click create email button
create_button = self.page.ele('xpath://button[@title="Create temporary email"]')
if create_button:
create_button.click()
time.sleep(1)
# click Create button in popup
modal_create_button = self.page.ele('xpath://button[contains(text(), "Create")]')
if modal_create_button:
modal_create_button.click()
time.sleep(2)
# get email address - modify selector
email_div = self.page.ele('xpath://div[@class="text-base sm:text-lg md:text-xl text-gray-700"]')
if email_div:
email = email_div.text.strip()
if '@' in email: # check if it's a valid email address
# check if domain is blocked
domain = email.split('@')[1]
if self.blocked_domains and domain in self.blocked_domains:
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.domain_blocked')}: {domain}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 域名已被屏蔽: {domain},尝试重新创建邮箱{Style.RESET_ALL}")
# create email again
return self.create_email()
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.create_success')}: {email}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 创建邮箱成功: {email}{Style.RESET_ALL}")
return email
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.create_failed')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建邮箱失败{Style.RESET_ALL}")
return None
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.create_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 创建邮箱出错: {str(e)}{Style.RESET_ALL}")
return None
def close(self):
"""close browser"""
if self.page:
self.page.quit()
def refresh_inbox(self):
"""refresh inbox"""
try:
if self.translator:
print(f"{Fore.CYAN}🔄 {self.translator.get('email.refreshing')}{Style.RESET_ALL}")
else:
print(f"{Fore.CYAN}🔄 正在刷新邮箱...{Style.RESET_ALL}")
# click refresh button
refresh_button = self.page.ele('xpath://button[@id="refresh"]')
if refresh_button:
refresh_button.click()
time.sleep(2) # wait for refresh to complete
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.refresh_success')}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 邮箱刷新成功{Style.RESET_ALL}")
return True
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.refresh_button_not_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 未找到刷新按钮{Style.RESET_ALL}")
return False
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.refresh_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 刷新邮箱出错: {str(e)}{Style.RESET_ALL}")
return False
def check_for_cursor_email(self):
"""检查是否有 Cursor 的验证邮件"""
try:
# find verification email - use more accurate selector
email_div = self.page.ele('xpath://div[contains(@class, "p-2") and contains(@class, "cursor-pointer") and contains(@class, "bg-white") and contains(@class, "shadow") and .//b[text()="no-reply@cursor.sh"] and .//span[text()="Verify your email address"]]')
if email_div:
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.verification_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 找到验证邮件{Style.RESET_ALL}")
# use JavaScript to click element
self.page.run_js('arguments[0].click()', email_div)
time.sleep(2) # wait for email content to load
return True
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_not_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 未找到验证邮件{Style.RESET_ALL}")
return False
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.verification_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 检查验证邮件出错: {str(e)}{Style.RESET_ALL}")
return False
def get_verification_code(self):
"""获取验证码"""
try:
# find verification code element
code_element = self.page.ele('xpath://td//div[contains(@style, "font-size:28px") and contains(@style, "letter-spacing:2px")]')
if code_element:
code = code_element.text.strip()
if code.isdigit() and len(code) == 6:
if self.translator:
print(f"{Fore.GREEN}{self.translator.get('email.verification_code_found')}: {code}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}✅ 获取验证码成功: {code}{Style.RESET_ALL}")
return code
if self.translator:
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.verification_code_not_found')}{Style.RESET_ALL}")
else:
print(f"{Fore.YELLOW}⚠️ 未找到有效的验证码{Style.RESET_ALL}")
return None
except Exception as e:
if self.translator:
print(f"{Fore.RED}{self.translator.get('email.verification_code_error')}: {str(e)}{Style.RESET_ALL}")
else:
print(f"{Fore.RED}❌ 获取验证码出错: {str(e)}{Style.RESET_ALL}")
return None
def main(translator=None):
temp_email = NewTempEmail(translator)
try:
email = temp_email.create_email()
if email:
if translator:
print(f"\n{Fore.CYAN}📧 {translator.get('email.address')}: {email}{Style.RESET_ALL}")
else:
print(f"\n{Fore.CYAN}📧 临时邮箱地址: {email}{Style.RESET_ALL}")
# test refresh function
while True:
if translator:
choice = input(f"\n{translator.get('email.refresh_prompt')}: ").lower()
else:
choice = input("\n按 R 刷新邮箱,按 Q 退出: ").lower()
if choice == 'r':
temp_email.refresh_inbox()
elif choice == 'q':
break
finally:
temp_email.close()
if __name__ == "__main__":
main()

View File

@@ -1,865 +0,0 @@
import os
from colorama import Fore, Style, init
import time
import random
import webbrowser
import sys
import json
from DrissionPage import ChromiumPage, ChromiumOptions
from cursor_auth import CursorAuth
from utils import get_random_wait_time, get_default_chrome_path
from config import get_config
import platform
# Initialize colorama
init()
# Define emoji constants
EMOJI = {
'START': '🚀',
'OAUTH': '🔑',
'SUCCESS': '',
'ERROR': '',
'WAIT': '',
'INFO': '',
'WARNING': '⚠️'
}
class OAuthHandler:
def __init__(self, translator=None, auth_type=None):
self.translator = translator
self.config = get_config(translator)
self.auth_type = auth_type # make sure the auth_type is not None
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser = None
self.selected_profile = None
def _get_available_profiles(self, user_data_dir):
"""Get list of available Chrome profiles with their names"""
try:
profiles = []
profile_names = {}
# Read Local State file to get profile names
local_state_path = os.path.join(user_data_dir, 'Local State')
if os.path.exists(local_state_path):
with open(local_state_path, 'r', encoding='utf-8') as f:
local_state = json.load(f)
info_cache = local_state.get('profile', {}).get('info_cache', {})
for profile_dir, info in info_cache.items():
profile_dir = profile_dir.replace('\\', '/')
if profile_dir == 'Default':
profile_names['Default'] = info.get('name', 'Default')
elif profile_dir.startswith('Profile '):
profile_names[profile_dir] = info.get('name', profile_dir)
# Get list of profile directories
for item in os.listdir(user_data_dir):
if item == 'Default' or (item.startswith('Profile ') and os.path.isdir(os.path.join(user_data_dir, item))):
profiles.append((item, profile_names.get(item, item)))
return sorted(profiles)
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
return []
def _select_profile(self):
"""Select a Chrome profile to use"""
try:
# Get available profiles
profiles = self._get_available_profiles(self._get_user_data_directory())
if not profiles:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('chrome_profile.no_profiles') if self.translator else 'No Chrome profiles found'}{Style.RESET_ALL}")
return False
# Display available profiles
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('chrome_profile.select_profile') if self.translator else 'Select a Chrome profile to use:'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{self.translator.get('chrome_profile.profile_list') if self.translator else 'Available profiles:'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit') if self.translator else 'Exit'}{Style.RESET_ALL}")
for i, (dir_name, display_name) in enumerate(profiles, 1):
print(f"{Fore.CYAN}{i}. {display_name} ({dir_name}){Style.RESET_ALL}")
# Get user selection
while True:
try:
choice = int(input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{len(profiles)}') if self.translator else f'Please enter your choice (0-{len(profiles)}): '}{Style.RESET_ALL}"))
if choice == 0: # Add quit
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.exiting') if self.translator else 'Exiting profile selection...'}{Style.RESET_ALL}")
return False
elif 1 <= choice <= len(profiles):
self.selected_profile = profiles[choice - 1][0]
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('chrome_profile.profile_selected', profile=self.selected_profile) if self.translator else f'Selected profile: {self.selected_profile}'}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
except ValueError:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
return False
def setup_browser(self):
"""Setup browser for OAuth flow using selected profile"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}")
# Platform-specific initialization
platform_name = platform.system().lower()
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
# Get browser paths and user data directory
user_data_dir = self._get_user_data_directory()
chrome_path = self._get_browser_path()
if not chrome_path:
raise Exception(f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}\n{self.translator.get('oauth.supported_browsers', platform=platform_name)}\n" +
"- Windows: Google Chrome, Chromium\n" +
"- macOS: Google Chrome, Chromium\n" +
"- Linux: Google Chrome, Chromium, chromium-browser")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=user_data_dir) if self.translator else f'Found browser data directory: {user_data_dir}'}{Style.RESET_ALL}")
# Show warning about closing Chrome first
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('chrome_profile.warning_chrome_close') if self.translator else 'Warning: This will close all running Chrome processes'}{Style.RESET_ALL}")
choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower()
if choice != 'y':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
# Kill existing browser processes
self._kill_browser_processes()
# Let user select a profile
if not self._select_profile():
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
return False
# Configure browser options
co = self._configure_browser_options(chrome_path, user_data_dir, self.selected_profile)
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=chrome_path) if self.translator else f'Starting browser at: {chrome_path}'}{Style.RESET_ALL}")
self.browser = ChromiumPage(co)
# Verify browser launched successfully
if not self.browser:
raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}")
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.browser_setup_completed') if self.translator else 'Browser setup completed successfully'}{Style.RESET_ALL}")
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_setup_failed', error=str(e)) if self.translator else f'Browser setup failed: {str(e)}'}{Style.RESET_ALL}")
if "DevToolsActivePort file doesn't exist" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_running_without_sudo_admin') if self.translator else 'Try running without sudo/administrator privileges'}{Style.RESET_ALL}")
elif "Chrome failed to start" in str(e):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
if platform_name == 'linux':
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
return False
def _kill_browser_processes(self):
"""Kill existing browser processes based on platform"""
try:
if os.name == 'nt': # Windows
processes = ['chrome.exe', 'chromium.exe']
for proc in processes:
os.system(f'taskkill /f /im {proc} >nul 2>&1')
else: # Linux/Mac
processes = ['chrome', 'chromium', 'chromium-browser']
for proc in processes:
os.system(f'pkill -f {proc} >/dev/null 2>&1')
time.sleep(1) # Wait for processes to close
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
def _get_user_data_directory(self):
"""Get the appropriate user data directory based on platform"""
try:
if os.name == 'nt': # Windows
possible_paths = [
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data'),
os.path.expandvars(r'%LOCALAPPDATA%\Chromium\User Data')
]
elif sys.platform == 'darwin': # macOS
possible_paths = [
os.path.expanduser('~/Library/Application Support/Google/Chrome'),
os.path.expanduser('~/Library/Application Support/Chromium')
]
else: # Linux
possible_paths = [
os.path.expanduser('~/.config/google-chrome'),
os.path.expanduser('~/.config/chromium'),
'/usr/bin/google-chrome',
'/usr/bin/chromium-browser'
]
# Try each possible path
for path in possible_paths:
if os.path.exists(path):
return path
# Create temporary profile if no existing profile found
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.creating_temporary_profile', path=temp_profile) if self.translator else f'Creating temporary profile at: {temp_profile}'}{Style.RESET_ALL}")
os.makedirs(temp_profile, exist_ok=True)
return temp_profile
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
raise
def _get_browser_path(self):
"""Get the browser executable path based on platform"""
try:
# Try default path first
chrome_path = get_default_chrome_path()
if chrome_path and os.path.exists(chrome_path):
return chrome_path
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
# Platform-specific paths
if os.name == 'nt': # Windows
alt_paths = [
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
r'C:\Program Files\Chromium\Application\chrome.exe',
os.path.expandvars(r'%ProgramFiles%\Google\Chrome\Application\chrome.exe'),
os.path.expandvars(r'%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe')
]
elif sys.platform == 'darwin': # macOS
alt_paths = [
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'/Applications/Chromium.app/Contents/MacOS/Chromium',
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
]
else: # Linux
alt_paths = [
'/usr/bin/google-chrome',
'/usr/bin/chromium-browser',
'/usr/bin/chromium',
'/snap/bin/chromium',
'/usr/local/bin/chrome',
'/usr/local/bin/chromium'
]
# Try each alternative path
for path in alt_paths:
expanded_path = os.path.expanduser(path)
if os.path.exists(expanded_path):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=expanded_path) if self.translator else f'Found browser at: {expanded_path}'}{Style.RESET_ALL}")
return expanded_path
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
return None
def _configure_browser_options(self, chrome_path, user_data_dir, active_profile):
"""Configure browser options based on platform"""
try:
co = ChromiumOptions()
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
co.set_argument(f'--profile-directory={active_profile}')
# Basic options
co.set_argument('--no-first-run')
co.set_argument('--no-default-browser-check')
co.set_argument('--disable-gpu')
# Platform-specific options
if sys.platform.startswith('linux'):
co.set_argument('--no-sandbox')
co.set_argument('--disable-dev-shm-usage')
co.set_argument('--disable-setuid-sandbox')
elif sys.platform == 'darwin':
co.set_argument('--disable-gpu-compositing')
elif os.name == 'nt':
co.set_argument('--disable-features=TranslateUI')
co.set_argument('--disable-features=RendererCodeIntegrity')
return co
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_configuring_browser_options', error=str(e)) if self.translator else f'Error configuring browser options: {e}'}{Style.RESET_ALL}")
raise
def handle_google_auth(self):
"""Handle Google OAuth authentication"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}")
# Setup browser
if not self.setup_browser():
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}")
return False, None
# Navigate to auth URL
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Look for Google auth button
selectors = [
"//a[contains(@href,'GoogleOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GoogleOAuth')]",
"(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback
]
auth_btn = None
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn and auth_btn.is_displayed():
break
except:
continue
if not auth_btn:
raise Exception("Could not find Google authentication button")
# Click the button and wait for page load
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Check if we're on account selection page
if "accounts.google.com" in self.browser.url:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
self.browser.run_js(f"""
alert('{alert_message}');
""")
except:
pass # Alert is optional
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success') if self.translator else 'Success'}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
if self.browser:
self.browser.quit()
except:
pass
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _wait_for_auth(self):
"""Wait for authentication to complete and extract auth info"""
try:
max_wait = 300 # 5 minutes
start_time = time.time()
check_interval = 2 # Check every 2 seconds
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.waiting_for_authentication', timeout='5 minutes') if self.translator else 'Waiting for authentication (timeout: 5 minutes)'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
# Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
token = None
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
if token:
# Get email from settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3)
email = None
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=email) if self.translator else f'Found email: {email}'}{Style.RESET_ALL}")
except:
email = "user@cursor.sh" # Fallback email
# Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
try:
parts = usage_str.split('/')
if len(parts) != 2:
return False
current = int(parts[0].strip())
limit = int(parts[1].strip())
return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
except:
return False
if check_usage_limits(usage_text):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
return {"email": email, "token": token}
# Also check URL as backup
if "cursor.com/settings" in self.browser.url:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_successful_login') if self.translator else 'Detected successful login'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication', error=str(e)) if self.translator else f'Waiting for authentication... ({str(e)})'}{Style.RESET_ALL}")
time.sleep(check_interval)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_waiting_for_authentication', error=str(e)) if self.translator else f'Error while waiting for authentication: {str(e)}'}{Style.RESET_ALL}")
return None
def handle_github_auth(self):
"""Handle GitHub OAuth authentication"""
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}")
# Setup browser
if not self.setup_browser():
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{Style.RESET_ALL}")
return False, None
# Navigate to auth URL
try:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_authentication_page') if self.translator else 'Navigating to authentication page...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Look for GitHub auth button
selectors = [
"//a[contains(@href,'GitHubOAuth')]",
"//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]",
"(//a[contains(@class,'auth-method-button')])[2]" # Second auth button as fallback
]
auth_btn = None
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2)
if auth_btn and auth_btn.is_displayed():
break
except:
continue
if not auth_btn:
raise Exception("Could not find GitHub authentication button")
# Click the button and wait for page load
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}")
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Wait for authentication to complete
auth_info = self._wait_for_auth()
if not auth_info:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}")
return False, None
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}")
return True, auth_info
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_error', error=str(e)) if self.translator else f'Authentication error: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
try:
if self.browser:
self.browser.quit()
except:
pass
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}")
return False, None
def _handle_oauth(self, auth_type):
"""Handle OAuth authentication for both Google and GitHub
Args:
auth_type (str): Type of authentication ('google' or 'github')
"""
try:
if not self.setup_browser():
return False, None
# Navigate to auth URL
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Set selectors based on auth type
if auth_type == "google":
selectors = [
"//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'][contains(@href,'GoogleOAuth')]",
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[1]"
]
else: # github
selectors = [
"(//a[@class='rt-reset rt-BaseButton rt-r-size-3 rt-variant-surface rt-high-contrast rt-Button auth-method-button_AuthMethodButton__irESX'])[2]"
]
# Wait for the button to be available
auth_btn = None
max_button_wait = 30 # 30 seconds
button_start_time = time.time()
while time.time() - button_start_time < max_button_wait:
for selector in selectors:
try:
auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1)
if auth_btn and auth_btn.is_displayed():
break
except:
continue
if auth_btn:
break
time.sleep(1)
if auth_btn:
# Click the button and wait for page load
auth_btn.click()
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
# Check if we're on account selection page
if auth_type == "google" and "accounts.google.com" in self.browser.url:
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
try:
self.browser.run_js(f"""
alert('{alert_message}');
""")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.alert_display_failed', error=str(e)) if self.translator else f'Alert display failed: {str(e)}'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_manually_to_continue_with_cursor_authentication') if self.translator else 'Please select your Google account manually to continue with Cursor authentication...'}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.waiting_for_authentication_to_complete') if self.translator else 'Waiting for authentication to complete...'}{Style.RESET_ALL}")
# Wait for authentication to complete
max_wait = 300 # 5 minutes
start_time = time.time()
last_url = self.browser.url
print(f"{Fore.CYAN}{EMOJI['WAIT']} {self.translator.get('oauth.checking_authentication_status') if self.translator else 'Checking authentication status...'}{Style.RESET_ALL}")
while time.time() - start_time < max_wait:
try:
# Check for authentication cookies
cookies = self.browser.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
if token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
# Navigate to settings page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.navigating_to_settings_page') if self.translator else 'Navigating to settings page...'}{Style.RESET_ALL}")
self.browser.get("https://www.cursor.com/settings")
time.sleep(3) # Wait for settings page to load
# Get email from settings page
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
try:
parts = usage_str.split('/')
if len(parts) != 2:
return False
current = int(parts[0].strip())
limit = int(parts[1].strip())
return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
except:
return False
if check_usage_limits(usage_text):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
# Also check URL as backup
current_url = self.browser.url
if "cursor.com/settings" in current_url:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.already_on_settings_page') if self.translator else 'Already on settings page!'}{Style.RESET_ALL}")
time.sleep(1)
cookies = self.browser.cookies()
for cookie in cookies:
if cookie.get("name") == "WorkosCursorSessionToken":
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
if token:
# Get email and check usage here too
try:
email_element = self.browser.ele("css:div[class='flex w-full flex-col gap-2'] div:nth-child(2) p:nth-child(2)")
if email_element:
actual_email = email_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_email', email=actual_email) if self.translator else f'Found email: {actual_email}'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_email', error=str(e)) if self.translator else f'Could not find email: {str(e)}'}{Style.RESET_ALL}")
actual_email = "user@cursor.sh"
# Check usage count
try:
usage_element = self.browser.ele("css:div[class='flex flex-col gap-4 lg:flex-row'] div:nth-child(1) div:nth-child(1) span:nth-child(2)")
if usage_element:
usage_text = usage_element.text
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.usage_count', usage=usage_text) if self.translator else f'Usage count: {usage_text}'}{Style.RESET_ALL}")
def check_usage_limits(usage_str):
try:
parts = usage_str.split('/')
if len(parts) != 2:
return False
current = int(parts[0].strip())
limit = int(parts[1].strip())
return (limit == 50 and current >= 50) or (limit == 150 and current >= 150)
except:
return False
if check_usage_limits(usage_text):
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.account_has_reached_maximum_usage', deleting='deleting') if self.translator else 'Account has reached maximum usage, deleting...'}{Style.RESET_ALL}")
if self._delete_current_account():
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_new_authentication_process') if self.translator else 'Starting new authentication process...'}{Style.RESET_ALL}")
if self.auth_type == "google":
return self.handle_google_auth()
else:
return self.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_expired_account') if self.translator else 'Failed to delete expired account'}{Style.RESET_ALL}")
else:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.account_is_still_valid', usage=usage_text) if self.translator else f'Account is still valid (Usage: {usage_text})'}{Style.RESET_ALL}")
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_check_usage_count', error=str(e)) if self.translator else f'Could not check usage count: {str(e)}'}{Style.RESET_ALL}")
# Remove the browser stay open prompt and input wait
return True, {"email": actual_email, "token": token}
elif current_url != last_url:
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.page_changed_checking_auth') if self.translator else 'Page changed, checking auth...'}{Style.RESET_ALL}")
last_url = current_url
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.status_check_error', error=str(e)) if self.translator else f'Status check error: {str(e)}'}{Style.RESET_ALL}")
time.sleep(1)
continue
time.sleep(1)
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}")
return False, None
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_button_not_found') if self.translator else 'Authentication button not found'}{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_failed', error=str(e)) if self.translator else f'Authentication failed: {str(e)}'}{Style.RESET_ALL}")
return False, None
finally:
if self.browser:
self.browser.quit()
def _extract_auth_info(self):
"""Extract authentication information after successful OAuth"""
try:
# Get cookies with retry
max_retries = 3
for attempt in range(max_retries):
try:
cookies = self.browser.cookies()
if cookies:
break
time.sleep(1)
except:
if attempt == max_retries - 1:
raise
time.sleep(1)
# Debug cookie information
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_cookies', count=len(cookies)) if self.translator else f'Found {len(cookies)} cookies'}{Style.RESET_ALL}")
email = None
token = None
for cookie in cookies:
name = cookie.get("name", "")
if name == "WorkosCursorSessionToken":
try:
value = cookie.get("value", "")
if "::" in value:
token = value.split("::")[-1]
elif "%3A%3A" in value:
token = value.split("%3A%3A")[-1]
except Exception as e:
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.token_extraction_error', error=str(e)) if self.translator else f'Token extraction error: {str(e)}'}{Style.RESET_ALL}")
elif name == "cursor_email":
email = cookie.get("value")
if email and token:
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful', email=email) if self.translator else f'Authentication successful - Email: {email}'}{Style.RESET_ALL}")
return True, {"email": email, "token": token}
else:
missing = []
if not email:
missing.append("email")
if not token:
missing.append("token")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) if self.translator else f'Missing authentication data: {", ".join(missing)}'}{Style.RESET_ALL}")
return False, None
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) if self.translator else f'Failed to extract auth info: {str(e)}'}{Style.RESET_ALL}")
return False, None
def _delete_current_account(self):
"""Delete the current account using the API"""
try:
delete_js = """
function deleteAccount() {
return new Promise((resolve, reject) => {
fetch('https://www.cursor.com/api/dashboard/delete-account', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include'
})
.then(response => {
if (response.status === 200) {
resolve('Account deleted successfully');
} else {
reject('Failed to delete account: ' + response.status);
}
})
.catch(error => {
reject('Error: ' + error);
});
});
}
return deleteAccount();
"""
result = self.browser.run_js(delete_js)
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}")
# Navigate back to auth page
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.redirecting_to_authenticator_cursor_sh') if self.translator else 'Redirecting to authenticator.cursor.sh...'}{Style.RESET_ALL}")
self.browser.get("https://authenticator.cursor.sh/sign-up")
time.sleep(get_random_wait_time(self.config, 'page_load_wait'))
return True
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}'}{Style.RESET_ALL}")
return False
def main(auth_type, translator=None):
"""Main function to handle OAuth authentication
Args:
auth_type (str): Type of authentication ('google' or 'github')
translator: Translator instance for internationalization
"""
handler = OAuthHandler(translator, auth_type)
if auth_type.lower() == 'google':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.google_start') if translator else 'Google start'}{Style.RESET_ALL}")
success, auth_info = handler.handle_google_auth()
elif auth_type.lower() == 'github':
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.github_start') if translator else 'Github start'}{Style.RESET_ALL}")
success, auth_info = handler.handle_github_auth()
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.invalid_authentication_type') if translator else 'Invalid authentication type'}{Style.RESET_ALL}")
return False
if success and auth_info:
# Update Cursor authentication
auth_manager = CursorAuth(translator)
if auth_manager.update_auth(
email=auth_info["email"],
access_token=auth_info["token"],
refresh_token=auth_info["token"]
):
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}")
# Close the browser after successful authentication
if handler.browser:
handler.browser.quit()
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}")
return False

View File

@@ -1,18 +0,0 @@
{
"manifest_version": 3,
"name": "Turnstile Patcher",
"version": "2.1",
"content_scripts": [
{
"js": [
"./script.js"
],
"matches": [
"<all_urls>"
],
"run_at": "document_start",
"all_frames": true,
"world": "MAIN"
}
]
}

View File

@@ -1,50 +0,0 @@
function qSelector(selector) {
return document.querySelector(selector);
}
(function() {
'use strict';
var solved = false;
var checkBoxClicked = false;
var requestCount = 0;
const MAX_ATTEMPTS = 1;
const CHECK_BOX = ".recaptcha-checkbox-border";
const AUDIO_BUTTON = "#recaptcha-audio-button";
const PLAY_BUTTON = ".rc-audiochallenge-play-button .rc-button-default";
const AUDIO_SOURCE = "#audio-source";
const IMAGE_SELECT = "#rc-imageselect";
const RESPONSE_FIELD = ".rc-audiochallenge-response-field";
const AUDIO_ERROR_MESSAGE = ".rc-audiochallenge-error-message";
const AUDIO_RESPONSE = "#audio-response";
const RELOAD_BUTTON = "#recaptcha-reload-button";
const RECAPTCHA_STATUS = "#recaptcha-accessible-status";
const DOSCAPTCHA = ".rc-doscaptcha-body";
const VERIFY_BUTTON = "#recaptcha-verify-button";
var recaptchaInitialStatus = qSelector(RECAPTCHA_STATUS) ? qSelector(RECAPTCHA_STATUS).innerText : ""
function isHidden(el) {
return(el.offsetParent === null)
}
try {
if(!checkBoxClicked && qSelector(CHECK_BOX) && !isHidden(qSelector(CHECK_BOX))) {
//console.log("checkbox clicked");
qSelector(CHECK_BOX).click();
checkBoxClicked = true;
}
//Check if the captcha is solved
if(qSelector(RECAPTCHA_STATUS) && (qSelector(RECAPTCHA_STATUS).innerText != recaptchaInitialStatus)) {
solved = true;
console.log("SOLVED");
}
if(requestCount > MAX_ATTEMPTS) {
console.log("Attempted Max Retries. Stopping the solver");
solved = true;
}
//Stop solving when Automated queries message is shown
if(qSelector(DOSCAPTCHA) && qSelector(DOSCAPTCHA).innerText.length > 0) {
console.log("Automated Queries Detected");
}
} catch(err) {
console.log(err.message);
console.log("An error occurred while solving. Stopping the solver.");
}
})();

View File

@@ -1,12 +0,0 @@
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// old method wouldn't work on 4k screens
let screenX = getRandomInt(800, 1200);
let screenY = getRandomInt(400, 600);
Object.defineProperty(MouseEvent.prototype, 'screenX', { value: screenX });
Object.defineProperty(MouseEvent.prototype, 'screenY', { value: screenY });

181
utils.py
View File

@@ -9,24 +9,171 @@ def get_user_documents_path():
return os.path.expanduser("~\\Documents")
else:
return os.path.expanduser("~/Documents")
def get_default_chrome_path():
"""Get default Chrome path"""
if sys.platform == "win32":
# Trying to find chrome in PATH
try:
import shutil
chrome_in_path = shutil.which("chrome")
if chrome_in_path:
return chrome_in_path
except:
pass
# Going to default path
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
elif sys.platform == "darwin":
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
def get_default_driver_path(browser_type='chrome'):
"""Get default driver path based on browser type"""
browser_type = browser_type.lower()
if browser_type == 'chrome':
return get_default_chrome_driver_path()
elif browser_type == 'edge':
return get_default_edge_driver_path()
elif browser_type == 'firefox':
return get_default_firefox_driver_path()
elif browser_type == 'brave':
# Brave 使用 Chrome 的 driver
return get_default_chrome_driver_path()
else:
return "/usr/bin/google-chrome"
# Default to Chrome if browser type is unknown
return get_default_chrome_driver_path()
def get_default_chrome_driver_path():
"""Get default Chrome driver path"""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver")
else:
return "/usr/local/bin/chromedriver"
def get_default_edge_driver_path():
"""Get default Edge driver path"""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver")
else:
return "/usr/local/bin/msedgedriver"
def get_default_firefox_driver_path():
"""Get default Firefox driver path"""
if sys.platform == "win32":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver.exe")
elif sys.platform == "darwin":
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver")
else:
return "/usr/local/bin/geckodriver"
def get_default_brave_driver_path():
"""Get default Brave driver path (uses Chrome driver)"""
# Brave 浏览器基于 Chromium所以使用相同的 chromedriver
return get_default_chrome_driver_path()
def get_default_browser_path(browser_type='chrome'):
"""Get default browser executable path"""
browser_type = browser_type.lower()
if sys.platform == "win32":
if browser_type == 'chrome':
# 尝试在 PATH 中找到 Chrome
try:
import shutil
chrome_in_path = shutil.which("chrome")
if chrome_in_path:
return chrome_in_path
except:
pass
# 使用默认路径
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
elif browser_type == 'edge':
return r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
elif browser_type == 'firefox':
return r"C:\Program Files\Mozilla Firefox\firefox.exe"
elif browser_type == 'opera':
# 尝试多个可能的 Opera 路径
opera_paths = [
r"C:\Program Files\Opera\opera.exe",
r"C:\Program Files (x86)\Opera\opera.exe",
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe')
]
for path in opera_paths:
if os.path.exists(path):
return path
return opera_paths[0] # 返回第一个路径,即使它不存在
elif browser_type == 'operagx':
# 尝试多个可能的 Opera GX 路径
operagx_paths = [
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe'),
r"C:\Program Files\Opera GX\opera.exe",
r"C:\Program Files (x86)\Opera GX\opera.exe"
]
for path in operagx_paths:
if os.path.exists(path):
return path
return operagx_paths[0] # 返回第一个路径,即使它不存在
elif browser_type == 'brave':
# Brave 浏览器的默认安装路径
paths = [
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe')
]
for path in paths:
if os.path.exists(path):
return path
return paths[0] # 返回第一个路径,即使它不存在
elif sys.platform == "darwin":
if browser_type == 'chrome':
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
elif browser_type == 'edge':
return "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
elif browser_type == 'firefox':
return "/Applications/Firefox.app/Contents/MacOS/firefox"
elif browser_type == 'brave':
return "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
elif browser_type == 'opera':
return "/Applications/Opera.app/Contents/MacOS/Opera"
elif browser_type == 'operagx':
return "/Applications/Opera GX.app/Contents/MacOS/Opera"
else: # Linux
if browser_type == 'chrome':
# 尝试多种可能的名称
chrome_names = ["google-chrome", "chrome", "chromium", "chromium-browser"]
for name in chrome_names:
try:
import shutil
path = shutil.which(name)
if path:
return path
except:
pass
return "/usr/bin/google-chrome"
elif browser_type == 'edge':
return "/usr/bin/microsoft-edge"
elif browser_type == 'firefox':
return "/usr/bin/firefox"
elif browser_type == 'opera':
return "/usr/bin/opera"
elif browser_type == 'operagx':
# 尝试常见的 Opera GX 路径
operagx_names = ["opera-gx"]
for name in operagx_names:
try:
import shutil
path = shutil.which(name)
if path:
return path
except:
pass
return "/usr/bin/opera-gx"
elif browser_type == 'brave':
# 尝试常见的 Brave 路径
brave_names = ["brave", "brave-browser"]
for name in brave_names:
try:
import shutil
path = shutil.which(name)
if path:
return path
except:
pass
return "/usr/bin/brave-browser"
# 如果找不到指定的浏览器类型,则返回 Chrome 的路径
return get_default_browser_path('chrome')
def get_linux_cursor_path():
"""Get Linux Cursor path"""