From f471450e128b79d8506ea82e252bcde667088c78 Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 12:01:18 +0800 Subject: [PATCH 01/30] Update README.md Unable to select next GitHub token from pool --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ca8d16..4c5b7d7 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,10 @@ [![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) [![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) + +[![Downloads](https://badgen.net/badge/Downloads/495k/green?icon=github)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee

From 84b77e8b13e017a9bf1a1847474aa1c7b7ab9ef1 Mon Sep 17 00:00:00 2001 From: Marcin Tyburski <167824447+Martyb166@users.noreply.github.com> Date: Mon, 14 Apr 2025 06:20:18 +0200 Subject: [PATCH 02/30] Create generator-generic-ossf-slsa3-publish.yml --- .../generator-generic-ossf-slsa3-publish.yml | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/generator-generic-ossf-slsa3-publish.yml diff --git a/.github/workflows/generator-generic-ossf-slsa3-publish.yml b/.github/workflows/generator-generic-ossf-slsa3-publish.yml new file mode 100644 index 0000000..35c829b --- /dev/null +++ b/.github/workflows/generator-generic-ossf-slsa3-publish.yml @@ -0,0 +1,66 @@ +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + +# This workflow lets you generate SLSA provenance file for your project. +# The generation satisfies level 3 for the provenance requirements - see https://slsa.dev/spec/v0.1/requirements +# The project is an initiative of the OpenSSF (openssf.org) and is developed at +# https://github.com/slsa-framework/slsa-github-generator. +# The provenance file can be verified using https://github.com/slsa-framework/slsa-verifier. +# For more information about SLSA and how it improves the supply-chain, visit slsa.dev. + +name: SLSA generic generator +on: + workflow_dispatch: + release: + types: [created] + +jobs: + build: + runs-on: ubuntu-latest + outputs: + digests: ${{ steps.hash.outputs.digests }} + + steps: + - uses: actions/checkout@v4 + + # ======================================================== + # + # Step 1: Build your artifacts. + # + # ======================================================== + - name: Build artifacts + run: | + # These are some amazing artifacts. + echo "artifact1" > artifact1 + echo "artifact2" > artifact2 + + # ======================================================== + # + # Step 2: Add a step to generate the provenance subjects + # as shown below. Update the sha256 sum arguments + # to include all binaries that you generate + # provenance for. + # + # ======================================================== + - name: Generate subject for provenance + id: hash + run: | + set -euo pipefail + + # List the artifacts the provenance will refer to. + files=$(ls artifact*) + # Generate the subjects (base64 encoded). + echo "hashes=$(sha256sum $files | base64 -w0)" >> "${GITHUB_OUTPUT}" + + provenance: + needs: [build] + permissions: + actions: read # To read the workflow path. + id-token: write # To sign the provenance. + contents: write # To add assets to a release. + uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.4.0 + with: + base64-subjects: "${{ needs.build.outputs.digests }}" + upload-assets: true # Optional: Upload to a new release From 80a76b507a2407d4be1a693ddb376593822923eb Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 12:28:00 +0800 Subject: [PATCH 03/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4c5b7d7..eabcd33 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ -[![Downloads](https://badgen.net/badge/Downloads/495k/green?icon=github)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee

From 75b17bb5155d3e56c61bec0781a68d6cc4a68cf5 Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 12:31:10 +0800 Subject: [PATCH 04/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eabcd33..3beb0de 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ -[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total_number.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee

From aa4177d5ec39ad6325ebbc0ad94e105cff8fce92 Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 12:35:12 +0800 Subject: [PATCH 05/30] Update README.md --- README.md | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3beb0de..3c218ec 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,9 @@

-[![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://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg)](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) - +[![Stars](https://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip.svg)](https://github.com/yeongpin/cursor-free-vip/stargazers) [![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total_number.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee From 046642182343bbfbcb55e0ea9c49d58f24d48633 Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 12:38:52 +0800 Subject: [PATCH 06/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3c218ec..5da7efe 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ [![Release](https://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg)](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://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip.svg)](https://github.com/yeongpin/cursor-free-vip/stargazers) +[![Stars](https://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip/stars.svg)](https://github.com/yeongpin/cursor-free-vip/stargazers) [![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total_number.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee From 431550e4a9160800bea269f402ba5cf882bb5ebf Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 12:42:45 +0800 Subject: [PATCH 07/30] Revert "Create generator-generic-ossf-slsa3-publish.yml" --- .../generator-generic-ossf-slsa3-publish.yml | 66 ------------------- 1 file changed, 66 deletions(-) delete mode 100644 .github/workflows/generator-generic-ossf-slsa3-publish.yml diff --git a/.github/workflows/generator-generic-ossf-slsa3-publish.yml b/.github/workflows/generator-generic-ossf-slsa3-publish.yml deleted file mode 100644 index 35c829b..0000000 --- a/.github/workflows/generator-generic-ossf-slsa3-publish.yml +++ /dev/null @@ -1,66 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow lets you generate SLSA provenance file for your project. -# The generation satisfies level 3 for the provenance requirements - see https://slsa.dev/spec/v0.1/requirements -# The project is an initiative of the OpenSSF (openssf.org) and is developed at -# https://github.com/slsa-framework/slsa-github-generator. -# The provenance file can be verified using https://github.com/slsa-framework/slsa-verifier. -# For more information about SLSA and how it improves the supply-chain, visit slsa.dev. - -name: SLSA generic generator -on: - workflow_dispatch: - release: - types: [created] - -jobs: - build: - runs-on: ubuntu-latest - outputs: - digests: ${{ steps.hash.outputs.digests }} - - steps: - - uses: actions/checkout@v4 - - # ======================================================== - # - # Step 1: Build your artifacts. - # - # ======================================================== - - name: Build artifacts - run: | - # These are some amazing artifacts. - echo "artifact1" > artifact1 - echo "artifact2" > artifact2 - - # ======================================================== - # - # Step 2: Add a step to generate the provenance subjects - # as shown below. Update the sha256 sum arguments - # to include all binaries that you generate - # provenance for. - # - # ======================================================== - - name: Generate subject for provenance - id: hash - run: | - set -euo pipefail - - # List the artifacts the provenance will refer to. - files=$(ls artifact*) - # Generate the subjects (base64 encoded). - echo "hashes=$(sha256sum $files | base64 -w0)" >> "${GITHUB_OUTPUT}" - - provenance: - needs: [build] - permissions: - actions: read # To read the workflow path. - id-token: write # To sign the provenance. - contents: write # To add assets to a release. - uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v1.4.0 - with: - base64-subjects: "${{ needs.build.outputs.digests }}" - upload-assets: true # Optional: Upload to a new release From abcc3a84fac37844d95fef9e11be033d9b0ea4f6 Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 12:52:45 +0800 Subject: [PATCH 08/30] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5da7efe..ea37bd4 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [![Release](https://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg)](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://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip/stars.svg)](https://github.com/yeongpin/cursor-free-vip/stargazers) -[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total_number.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee

From 7d355f126c130a29f5bc314a26ca3372f4848d4c Mon Sep 17 00:00:00 2001 From: yeongpin Date: Mon, 14 Apr 2025 13:08:32 +0800 Subject: [PATCH 09/30] 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. --- CHANGELOG.md | 10 +++++++++- config.py | 13 ++++++------- oauth_auth.py | 9 ++++++--- utils.py | 30 +++++++++++++++++++++++++++--- 4 files changed, 48 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f0f41..4d29a06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,14 @@ # Change Log -## v1.9.03 +## 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 編輯器重定向到登出頁面並自動登出 diff --git a/config.py b/config.py index 400353d..38cc332 100644 --- a/config.py +++ b/config.py @@ -60,18 +60,17 @@ def setup_config(translator=None): 'Browser': { 'default_browser': 'chrome', 'chrome_path': get_default_browser_path('chrome'), - 'edge_path': get_default_browser_path('edge'), - 'firefox_path': get_default_browser_path('firefox'), - 'brave_path': get_default_browser_path('brave'), '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') - }, - 'Chrome': { - 'chromepath': get_default_browser_path('chrome') + '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', diff --git a/oauth_auth.py b/oauth_auth.py index f70e243..a9ae3b1 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -305,7 +305,8 @@ class OAuthHandler: 'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'), 'edge': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Microsoft', 'Edge', 'User Data'), 'firefox': os.path.join(os.environ.get('APPDATA', ''), 'Mozilla', 'Firefox', 'Profiles'), - 'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable') + 'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable'), + 'operagx': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera GX Stable') } elif sys.platform == 'darwin': # macOS user_data_dirs = { @@ -313,7 +314,8 @@ class OAuthHandler: 'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'), 'edge': os.path.expanduser('~/Library/Application Support/Microsoft Edge'), 'firefox': os.path.expanduser('~/Library/Application Support/Firefox/Profiles'), - 'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera') + 'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'), + 'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX') } else: # Linux user_data_dirs = { @@ -321,7 +323,8 @@ class OAuthHandler: 'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'), 'edge': os.path.expanduser('~/.config/microsoft-edge'), 'firefox': os.path.expanduser('~/.mozilla/firefox'), - 'opera': os.path.expanduser('~/.config/opera') + 'opera': os.path.expanduser('~/.config/opera'), + 'operagx': os.path.expanduser('~/.config/opera-gx') } # 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的 diff --git a/utils.py b/utils.py index 8a66dbd..8922f86 100644 --- a/utils.py +++ b/utils.py @@ -84,14 +84,24 @@ def get_default_browser_path(browser_type='chrome'): 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'), - os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'), - os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.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 = [ @@ -115,6 +125,8 @@ def get_default_browser_path(browser_type='chrome'): 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': @@ -135,6 +147,18 @@ def get_default_browser_path(browser_type='chrome'): 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"] From 1e72e59985381766cb03890bb0bf59fb0fe0d9e4 Mon Sep 17 00:00:00 2001 From: yeongpin Date: Mon, 14 Apr 2025 13:49:02 +0800 Subject: [PATCH 10/30] Update version in .env file to 1.9.04 --- .env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.env b/.env index 05af5ad..7ca6fbf 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -version=1.9.03 -VERSION=1.9.03 +version=1.9.04 +VERSION=1.9.04 From a730b145a1fa5d2354eb02bbbd182f4a4f68d8a8 Mon Sep 17 00:00:00 2001 From: alanwang Date: Mon, 14 Apr 2025 14:18:58 +0800 Subject: [PATCH 11/30] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8match-case?= =?UTF-8?q?=E9=87=8D=E6=9E=84=E8=AF=AD=E8=A8=80=E6=98=A0=E5=B0=84=E5=92=8C?= =?UTF-8?q?=E8=8F=9C=E5=8D=95=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将语言映射和菜单选择逻辑从if-elif重构为match-case,提高代码可读性和维护性 --- main.py | 284 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 147 insertions(+), 137 deletions(-) diff --git a/main.py b/main.py index e3555a9..c3759db 100644 --- a/main.py +++ b/main.py @@ -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() @@ -136,53 +142,56 @@ class Translator: 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' @@ -566,87 +575,88 @@ def main(): choice_num = 17 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 cursor_register + cursor_register.main(translator) + print_menu() + case "3": + import cursor_register_google + cursor_register_google.main(translator) + print_menu() + case "4": + import cursor_register_github + cursor_register_github.main(translator) + print_menu() + case "5": + import cursor_register_manual + cursor_register_manual.main(translator) + print_menu() + case "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() + case "7": + import quit_cursor + quit_cursor.quit_cursor(translator) + print_menu() + case "8": + if select_language(): + print_menu() + continue + case "9": + import disable_auto_update + disable_auto_update.run(translator) + print_menu() + case "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() + case "11": + import logo + print(logo.CURSOR_CONTRIBUTORS) + print_menu() + case "12": + from config import print_config + print_config(get_config(), translator) + print_menu() + case "13": + from oauth_auth import OAuthHandler + oauth = OAuthHandler(translator) + oauth._select_profile() + print_menu() + case "14": + import delete_cursor_google + delete_cursor_google.main(translator) + print_menu() + case "15": + import bypass_version + bypass_version.main(translator) + print_menu() + case "16": + import check_user_authorized + check_user_authorized.main(translator) + print_menu() + case "17": + 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() - elif choice == "17": - import bypass_token_limit - bypass_token_limit.run(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: From f541bc40b4edff06c53b47b63b39288477dc5127 Mon Sep 17 00:00:00 2001 From: haroondilshad Date: Mon, 14 Apr 2025 08:20:55 +0200 Subject: [PATCH 12/30] =?UTF-8?q?fix:=20=E2=9D=8C=20An=20error=20occurred:?= =?UTF-8?q?=20f-string=20expression=20part=20cannot=20include=20a=20backsl?= =?UTF-8?q?ash=20(delete=5Fcursor=5Fgoogle.py,=20line=20243)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cursor_register_manual.py | 2 +- delete_cursor_google.py | 6 +++--- oauth_auth.py | 25 +++++++++++++++++-------- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/cursor_register_manual.py b/cursor_register_manual.py index d33df91..e0f8b3e 100644 --- a/cursor_register_manual.py +++ b/cursor_register_manual.py @@ -79,7 +79,7 @@ class CursorRegistration: 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}") + print(f"{Fore.CYAN}{EMOJI['MAIL']} {self.translator.get('register.email_address')}: {self.email_address}" + "\n" + f"{Style.RESET_ALL}") return True except Exception as e: diff --git a/delete_cursor_google.py b/delete_cursor_google.py index baa233f..3bffac9 100644 --- a/delete_cursor_google.py +++ b/delete_cursor_google.py @@ -247,11 +247,11 @@ class CursorGoogleAccountDeleter(OAuthHandler): except: # Try direct JavaScript input as fallback try: - self.browser.run_js(f""" + self.browser.run_js(r""" arguments[0].value = "Delete"; - const event = new Event('input', {{ bubbles: true }}); + const event = new Event('input', { bubbles: true }); arguments[0].dispatchEvent(event); - const changeEvent = new Event('change', {{ bubbles: true }}); + 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}") diff --git a/oauth_auth.py b/oauth_auth.py index a9ae3b1..8a2185f 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -176,10 +176,15 @@ class OAuthHandler: browser_path = self._get_browser_path() if not browser_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") + error_msg = ( + f"{self.translator.get('oauth.no_compatible_browser_found') if self.translator else 'No compatible browser found. Please install Google Chrome or Chromium.'}" + + "\n" + + f"{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" + ) + raise Exception(error_msg) 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}") @@ -958,7 +963,8 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) 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}") + error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) + print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") elif name == "cursor_email": email = cookie.get("value") @@ -971,11 +977,13 @@ class OAuthHandler: 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}") + error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) + print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{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}") + error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) + print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False, None def _delete_current_account(self): @@ -1017,7 +1025,8 @@ class OAuthHandler: 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}") + error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e)) + print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False def main(auth_type, translator=None): From 96704e9f38b869866e64431bd3bf7d1b097dd3e6 Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 14:21:42 +0800 Subject: [PATCH 13/30] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ea37bd4..f08e38d 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@

-[![Release](https://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Release](https://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg?)](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://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip/stars.svg)](https://github.com/yeongpin/cursor-free-vip/stargazers) -[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Stars](https://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip/stars.svg?)](https://github.com/yeongpin/cursor-free-vip/stargazers) +[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg?)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee

From ee287b91f28154f30700b8f4e5c1b09000f68d0f Mon Sep 17 00:00:00 2001 From: alanwang Date: Mon, 14 Apr 2025 14:31:06 +0800 Subject: [PATCH 14/30] =?UTF-8?q?ci:=20=E6=9B=B4=E6=96=B0=20ARM64=20Docker?= =?UTF-8?q?=20=E5=AE=B9=E5=99=A8=E4=B8=AD=E7=9A=84=20Python=20=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E8=87=B3=203.10?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将 ARM64 Docker 容器中的 Python 版本从 3.9 更新至 3.10,以使用最新的稳定版本并确保兼容性 --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1daddd0..6395afc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -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 From 3cca2e3b17ff6a5530a6761fd3ba1a7f1b3af695 Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 15:01:06 +0800 Subject: [PATCH 15/30] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f08e38d..3e560a7 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@

-[![Release](https://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg?)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Release](https://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg?nocache=1)](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://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip/stars.svg?)](https://github.com/yeongpin/cursor-free-vip/stargazers) -[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg?)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Stars](https://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip/stars.svg?nocache=1)](https://github.com/yeongpin/cursor-free-vip/stargazers) +[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg?nocache=1)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee

From 34a23a69a15b2a74af77663a391fc7d1c632607b Mon Sep 17 00:00:00 2001 From: Pin Studios Date: Mon, 14 Apr 2025 15:24:40 +0800 Subject: [PATCH 16/30] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 3e560a7..9489b17 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,10 @@

-[![Release](https://www.pinnumber.rr.nu/github/release/yeongpin/cursor-free-vip.svg?nocache=1)](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://www.pinnumber.rr.nu/github/stars/yeongpin/cursor-free-vip/stars.svg?nocache=1)](https://github.com/yeongpin/cursor-free-vip/stargazers) -[![Downloads](https://www.pinnumber.rr.nu/github/downloads/yeongpin/cursor-free-vip/total.svg?nocache=1)](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) Buy Me a Coffee

From 7e0da4a0cb0ebeb46383453c1df00599f9621d30 Mon Sep 17 00:00:00 2001 From: yeongpin Date: Mon, 14 Apr 2025 16:24:50 +0800 Subject: [PATCH 17/30] Update version to 1.9.05 and enhance CHANGELOG with new features, fixes, and Python version update in Docker container. --- .env | 4 ++-- CHANGELOG.md | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/.env b/.env index 7ca6fbf..8449e55 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -version=1.9.04 -VERSION=1.9.04 +version=1.9.05 +VERSION=1.9.05 diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d29a06..6c9effa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Change Log +## 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. Fix: Some Issues | 修復一些問題 + ## v1.9.04 1. Add: Opera GX Support | 添加 Opera GX 支持 2. Same as v1.9.03 | 與 v1.9.03 相同 From d3c6bf227bddb5d0006cb21f001acd85416061e2 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 21:00:58 +0800 Subject: [PATCH 18/30] sync: aur version --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index 97c5636..7bd7734 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -2,7 +2,7 @@ # Contributor: Canmi (Canmi21) pkgname=cursor-free-vip-git -pkgver=1.9.03.2.g43a58db +pkgver=1.9.04 pkgrel=1 pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit" arch=('x86_64') From 2f012b9dc5eea6866785ac2ffcd10023c9c9fac2 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 21:01:47 +0800 Subject: [PATCH 19/30] fix: missing LICENSE --- PKGBUILD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PKGBUILD b/PKGBUILD index 7bd7734..5ae8e8f 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -7,7 +7,7 @@ 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') +license=('MIT' 'Attribution-NonCommercial-NoDerivatives 4.0 International') depends=('python' 'cursor-bin') makedepends=('git' 'python' 'pyinstaller' 'uv') provides=('cursor-free-vip') From e69e500e8d65b24d948ceff43c1ae404b074b6d5 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 21:02:34 +0800 Subject: [PATCH 20/30] add: LICENSE install --- PKGBUILD | 2 ++ 1 file changed, 2 insertions(+) diff --git a/PKGBUILD b/PKGBUILD index 5ae8e8f..847b714 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -29,4 +29,6 @@ build() { package() { install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip" + install -Dm644 "$srcdir/cursor-free-vip/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" } \ No newline at end of file From d867f5cfe9730e309452c6021c6f23617ff63bdc Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 21:02:50 +0800 Subject: [PATCH 21/30] update: SRCINFO --- .SRCINFO | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.SRCINFO b/.SRCINFO index d143943..6ba16b7 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,10 +1,11 @@ pkgbase = cursor-free-vip-git pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit - pkgver = 1.9.03.2.g43a58db + pkgver = 1.9.04 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 From b98059a476d8b4b147b3b411b6dc1bcc619fad8f Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 21:39:20 +0800 Subject: [PATCH 22/30] fix: missspell --- .SRCINFO | 2 +- PKGBUILD | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.SRCINFO b/.SRCINFO index 6ba16b7..f5b9776 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,7 +1,7 @@ pkgbase = cursor-free-vip-git pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit pkgver = 1.9.04 - pkgrel = 1 + pkgrel = 2 url = https://github.com/yeongpin/cursor-free-vip arch = x86_64 license = MIT diff --git a/PKGBUILD b/PKGBUILD index 847b714..72cd8ca 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -3,7 +3,7 @@ pkgname=cursor-free-vip-git pkgver=1.9.04 -pkgrel=1 +pkgrel=2 pkgdesc="Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit" arch=('x86_64') url="https://github.com/yeongpin/cursor-free-vip" @@ -29,6 +29,6 @@ build() { package() { install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip" - install -Dm644 "$srcdir/cursor-free-vip/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license" + 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" } \ No newline at end of file From 4b9c465dd53fd967e7b858b5ee2600b9d3cb3d65 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 21:49:54 +0800 Subject: [PATCH 23/30] fix: path --- PKGBUILD | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PKGBUILD b/PKGBUILD index 72cd8ca..be2a618 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -28,7 +28,7 @@ build() { } package() { - install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip" - install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license" + install -Dm644 "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" } \ No newline at end of file From b42b4b01b94f3a556c26125a390d6a5f6f128867 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 22:02:10 +0800 Subject: [PATCH 24/30] fix: fetch --- .SRCINFO | 6 ++++-- PKGBUILD | 10 +++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/.SRCINFO b/.SRCINFO index f5b9776..a732162 100644 --- a/.SRCINFO +++ b/.SRCINFO @@ -1,7 +1,7 @@ pkgbase = cursor-free-vip-git pkgdesc = Reset Cursor AI MachineID & Auto Sign Up / In & Bypass Higher Token Limit - pkgver = 1.9.04 - pkgrel = 2 + pkgver = 1.9.04.10.g5863891 + pkgrel = 1 url = https://github.com/yeongpin/cursor-free-vip arch = x86_64 license = MIT @@ -14,6 +14,8 @@ pkgbase = cursor-free-vip-git 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 diff --git a/PKGBUILD b/PKGBUILD index be2a618..1b81dda 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -2,8 +2,8 @@ # Contributor: Canmi (Canmi21) pkgname=cursor-free-vip-git -pkgver=1.9.04 -pkgrel=2 +pkgver=1.9.04.10.g5863891 +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" @@ -11,8 +11,8 @@ 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") -sha256sums=('SKIP') +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" @@ -28,7 +28,7 @@ build() { } package() { - install -Dm644 "LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license" + 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" } \ No newline at end of file From 21535104a6b60d4aea69a3f5e59d2c61ba0b9dd4 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 23:04:43 +0800 Subject: [PATCH 25/30] fix: google-chrome-stable on archlinx(linux), killall, port, and debug --- oauth_auth.py | 40 ++++++++++++---------------------------- 1 file changed, 12 insertions(+), 28 deletions(-) diff --git a/oauth_auth.py b/oauth_auth.py index 8a2185f..4313041 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -1,3 +1,4 @@ +# oauth_auth.py import os from colorama import Fore, Style, init import time @@ -30,7 +31,7 @@ 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 + self.auth_type = auth_type os.environ['BROWSER_HEADLESS'] = 'False' self.browser = None self.selected_profile = None @@ -182,7 +183,7 @@ class OAuthHandler: f"{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" + "- Linux: Google Chrome, Chromium, google-chrome-stable" ) raise Exception(error_msg) @@ -244,7 +245,7 @@ class OAuthHandler: browser_processes = { 'chrome': { 'win': ['chrome.exe', 'chromium.exe'], - 'linux': ['chrome', 'chromium', 'chromium-browser'], + 'linux': ['chrome', 'chromium', 'chromium-browser', 'google-chrome-stable'], 'mac': ['Chrome', 'Chromium'] }, 'brave': { @@ -428,7 +429,12 @@ class OAuthHandler: elif browser_type == 'firefox': possible_paths = ['/usr/bin/firefox'] else: # 默认为 Chrome - possible_paths = ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser'] + possible_paths = [ + '/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable + '/usr/bin/google-chrome', + '/usr/bin/chromium', + '/usr/bin/chromium-browser' + ] # 检查每个可能的路径 for path in possible_paths: @@ -436,7 +442,7 @@ class OAuthHandler: print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}") return path - # 如果找不到指定浏览器,则尝试使用Chrome + # 如果找不到指定浏览器,则尝试使用 Chrome if browser_type != 'chrome': print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}") return self._get_chrome_path() @@ -446,29 +452,6 @@ class OAuthHandler: 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 _get_chrome_path(self): - """Fallback method to get Chrome path""" - try: - if os.name == 'nt': # Windows - possible_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': # macOS - possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'] - else: # Linux - possible_paths = ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser'] - - for path in possible_paths: - if os.path.exists(path): - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_chrome_at', path=path) if self.translator else f'Found Chrome at: {path}'}{Style.RESET_ALL}") - return path - return None - except Exception as e: - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_chrome_path', error=str(e)) if self.translator else f'Error finding Chrome path: {e}'}{Style.RESET_ALL}") - return None def _configure_browser_options(self, browser_path, user_data_dir, active_profile): """Configure browser options based on platform""" @@ -481,6 +464,7 @@ class OAuthHandler: co.set_argument('--no-first-run') co.set_argument('--no-default-browser-check') co.set_argument('--disable-gpu') + co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口 # Platform-specific options if sys.platform.startswith('linux'): From 6eba95c0556d3772002b6fc25ff46be60e98f52a Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 23:05:10 +0800 Subject: [PATCH 26/30] fix: oauth logic, add more debug output --- oauth_auth.py | 377 +++++++++++++++++++++++++++++--------------------- 1 file changed, 220 insertions(+), 157 deletions(-) diff --git a/oauth_auth.py b/oauth_auth.py index 4313041..0c518a3 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -6,6 +6,7 @@ import random import webbrowser import sys import json +import logging # Added for detailed logging from DrissionPage import ChromiumPage, ChromiumOptions from cursor_auth import CursorAuth from utils import get_random_wait_time, get_default_browser_path @@ -16,6 +17,16 @@ from get_user_token import get_token_from_cookie # Initialize colorama init() +# Set up logging +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.StreamHandler(sys.stdout) + ] +) +logger = logging.getLogger(__name__) + # Define emoji constants EMOJI = { 'START': '🚀', @@ -61,6 +72,7 @@ class OAuthHandler: profiles.append((item, profile_names.get(item, item))) return sorted(profiles) except Exception as e: + logger.error(f"Error loading Chrome profiles: {str(e)}") # Enhanced logging 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 [] @@ -73,7 +85,6 @@ class OAuthHandler: browser_type_display = browser_type.capitalize() if self.translator: - # 动态使用浏览器类型 print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('browser_profile.select_profile', browser=browser_type_display)}{Style.RESET_ALL}") print(f"{Fore.CYAN}{self.translator.get('browser_profile.profile_list', browser=browser_type_display)}{Style.RESET_ALL}") else: @@ -91,19 +102,15 @@ class OAuthHandler: state_data = json.load(f) profiles_data = state_data.get('profile', {}).get('info_cache', {}) - # Create a list of available profiles profiles = [] for profile_id, profile_info in profiles_data.items(): name = profile_info.get('name', profile_id) - # Mark the default profile if profile_id.lower() == 'default': name = f"{name} (Default)" profiles.append((profile_id, name)) - # Sort profiles by name profiles.sort(key=lambda x: x[1]) - # Show available profiles if self.translator: print(f"{Fore.CYAN}0. {self.translator.get('menu.exit')}{Style.RESET_ALL}") else: @@ -112,7 +119,6 @@ class OAuthHandler: for i, (profile_id, name) in enumerate(profiles, 1): print(f"{Fore.CYAN}{i}. {name}{Style.RESET_ALL}") - # Get user's choice max_choice = len(profiles) choice_str = input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{max_choice}') if self.translator else f'Please enter your choice (0-{max_choice})'}{Style.RESET_ALL}") @@ -142,19 +148,18 @@ class OAuthHandler: print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}") return self._select_profile() else: - # No Local State file, use Default profile print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('browser_profile.no_profiles', browser=browser_type_display) if self.translator else f'No {browser_type_display} profiles found'}{Style.RESET_ALL}") self.selected_profile = "Default" return True except Exception as e: - # Error loading profiles, use Default profile + logger.error(f"Error loading profiles: {str(e)}") # Enhanced logging print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}") self.selected_profile = "Default" return True except Exception as e: - # General error, use Default profile + logger.error(f"Profile selection error: {str(e)}") # Enhanced logging print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}") self.selected_profile = "Default" return True @@ -162,17 +167,16 @@ class OAuthHandler: def setup_browser(self): """Setup browser for OAuth flow using selected profile""" try: + logger.info("Initializing browser setup") # Enhanced logging 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() + logger.info(f"Detected platform: {platform_name}") # Enhanced logging 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}") - # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') - # Get browser paths and user data directory user_data_dir = self._get_user_data_directory() browser_path = self._get_browser_path() @@ -185,11 +189,12 @@ class OAuthHandler: "- macOS: Google Chrome, Chromium\n" + "- Linux: Google Chrome, Chromium, google-chrome-stable" ) + logger.error("No compatible browser found") # Enhanced logging raise Exception(error_msg) + logger.info(f"Found browser data directory: {user_data_dir}") # Enhanced logging 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 browser first - 使用动态提示 if self.translator: warning_msg = self.translator.get('oauth.warning_browser_close', browser=browser_type) else: @@ -199,31 +204,33 @@ class OAuthHandler: choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower() if choice != 'y': + logger.info("Operation cancelled by user") # Enhanced logging 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(): + logger.info("Operation cancelled by user during profile selection") # Enhanced logging 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(browser_path, user_data_dir, self.selected_profile) + logger.info(f"Starting browser at: {browser_path}") # Enhanced logging print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_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', error=str(e)) if self.translator else 'Failed to initialize browser instance'}") + logger.error("Failed to initialize browser instance") # Enhanced logging + raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}") + logger.info("Browser setup completed successfully") # Enhanced logging 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: + logger.error(f"Browser setup failed: {str(e)}", exc_info=True) # Enhanced logging with stack trace 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}") @@ -236,12 +243,10 @@ class OAuthHandler: def _kill_browser_processes(self): """Kill existing browser processes based on platform and browser type""" try: - # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type = browser_type.lower() - # 根据浏览器类型和平台定义要关闭的进程 browser_processes = { 'chrome': { 'win': ['chrome.exe', 'chromium.exe'], @@ -270,7 +275,6 @@ class OAuthHandler: } } - # 获取平台类型 if os.name == 'nt': platform_type = 'win' elif sys.platform == 'darwin': @@ -278,34 +282,31 @@ class OAuthHandler: else: platform_type = 'linux' - # 获取要关闭的进程列表 processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, []) - if self.translator: - print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}") + logger.info(f"Killing {browser_type} processes: {processes}") # Enhanced logging + print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}") - # 根据平台关闭进程 - if os.name == 'nt': # Windows + if os.name == 'nt': for proc in processes: os.system(f'taskkill /f /im {proc} >nul 2>&1') - else: # Linux/Mac + else: for proc in processes: os.system(f'pkill -f {proc} >/dev/null 2>&1') - time.sleep(1) # Wait for processes to close + time.sleep(1) except Exception as e: + logger.warning(f"Could not kill browser processes: {str(e)}") # Enhanced logging 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 default user data directory based on browser type and platform""" try: - # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type = browser_type.lower() - # 根据操作系统和浏览器类型获取用户数据目录 - if os.name == 'nt': # Windows + if os.name == 'nt': user_data_dirs = { 'chrome': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data'), 'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'), @@ -314,7 +315,7 @@ class OAuthHandler: 'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable'), 'operagx': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera GX Stable') } - elif sys.platform == 'darwin': # macOS + elif sys.platform == 'darwin': user_data_dirs = { 'chrome': os.path.expanduser('~/Library/Application Support/Google/Chrome'), 'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'), @@ -323,7 +324,7 @@ class OAuthHandler: 'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'), 'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX') } - else: # Linux + else: user_data_dirs = { 'chrome': os.path.expanduser('~/.config/google-chrome'), 'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'), @@ -333,19 +334,20 @@ class OAuthHandler: 'operagx': os.path.expanduser('~/.config/opera-gx') } - # 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的 user_data_dir = user_data_dirs.get(browser_type) if user_data_dir and os.path.exists(user_data_dir): + logger.info(f"Found {browser_type} user data directory: {user_data_dir}") # Enhanced logging print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}") return user_data_dir else: + logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome") # Enhanced logging print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}") - return user_data_dirs['chrome'] # 回退到 Chrome 目录 + return user_data_dirs['chrome'] except Exception as e: + logger.error(f"Error getting user data directory: {str(e)}") # Enhanced logging 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}") - # 在出错时提供一个默认目录 if os.name == 'nt': return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data') elif sys.platform == 'darwin': @@ -356,26 +358,24 @@ class OAuthHandler: def _get_browser_path(self): """Get appropriate browser path based on platform and selected browser type""" try: - # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type = browser_type.lower() - # 首先检查配置中是否有明确指定的浏览器路径 browser_path = config.get('Browser', f'{browser_type}_path', fallback=None) if browser_path and os.path.exists(browser_path): + logger.info(f"Using configured {browser_type} path: {browser_path}") # Enhanced logging print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}") return browser_path - # 尝试获取默认路径 browser_path = get_default_browser_path(browser_type) if browser_path and os.path.exists(browser_path): return browser_path + logger.info("Searching for alternative browser installations") # Enhanced logging 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}") - # 如果未找到配置中指定的浏览器,则尝试查找其他兼容浏览器 - if os.name == 'nt': # Windows + if os.name == 'nt': possible_paths = [] if browser_type == 'brave': possible_paths = [ @@ -400,16 +400,17 @@ class OAuthHandler: os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'), os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe'), os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'), - os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe') + os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Oper +a GX', 'opera.exe') ] - else: # 默认为 Chrome + else: possible_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': # macOS + elif sys.platform == 'darwin': possible_paths = [] if browser_type == 'brave': possible_paths = ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser'] @@ -417,10 +418,10 @@ class OAuthHandler: possible_paths = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'] elif browser_type == 'firefox': possible_paths = ['/Applications/Firefox.app/Contents/MacOS/firefox'] - else: # 默认为 Chrome + else: possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'] - else: # Linux + else: possible_paths = [] if browser_type == 'brave': possible_paths = ['/usr/bin/brave-browser', '/usr/bin/brave'] @@ -428,28 +429,29 @@ class OAuthHandler: possible_paths = ['/usr/bin/microsoft-edge'] elif browser_type == 'firefox': possible_paths = ['/usr/bin/firefox'] - else: # 默认为 Chrome + else: possible_paths = [ - '/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable + '/usr/bin/google-chrome-stable', '/usr/bin/google-chrome', '/usr/bin/chromium', '/usr/bin/chromium-browser' ] - # 检查每个可能的路径 for path in possible_paths: if os.path.exists(path): + logger.info(f"Found browser at: {path}") # Enhanced logging print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}") return path - # 如果找不到指定浏览器,则尝试使用 Chrome if browser_type != 'chrome': + logger.warning(f"Could not find {browser_type}, trying Chrome") # Enhanced logging print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}") return self._get_chrome_path() return None except Exception as e: + logger.error(f"Error finding browser path: {str(e)}", exc_info=True) # Enhanced logging 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 @@ -460,13 +462,11 @@ class OAuthHandler: co.set_paths(browser_path=browser_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') - co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口 + co.set_argument('--remote-debugging-port=9222') - # Platform-specific options if sys.platform.startswith('linux'): co.set_argument('--no-sandbox') co.set_argument('--disable-dev-shm-usage') @@ -477,57 +477,67 @@ class OAuthHandler: co.set_argument('--disable-features=TranslateUI') co.set_argument('--disable-features=RendererCodeIntegrity') + logger.info("Browser options configured successfully") # Enhanced logging return co except Exception as e: + logger.error(f"Error configuring browser options: {str(e)}", exc_info=True) # Enhanced logging 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: + logger.info("Starting Google OAuth authentication") # Enhanced logging 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(): + logger.error("Browser failed to initialize") # Enhanced logging 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: + logger.info("Navigating to authentication page") # Enhanced logging 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')) + logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging - # 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 + "(//a[contains(@class,'auth-method-button')])[1]" ] auth_btn = None for selector in selectors: + logger.debug(f"Trying selector: {selector}") # Enhanced logging try: - auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2) + auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout if auth_btn and auth_btn.is_displayed(): + logger.info(f"Found Google auth button with selector: {selector}") # Enhanced logging break - except: + except Exception as e: + logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging continue if not auth_btn: + logger.error("Could not find Google authentication button") # Enhanced logging raise Exception("Could not find Google authentication button") - # Click the button and wait for page load + logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging 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() + try: + auth_btn.click() + except Exception as e: + logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging + self.browser.run_js("arguments[0].click();", auth_btn) 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: + logger.info("On Google account selection page") # Enhanced logging 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 选项 config = get_config(self.translator) show_alert = config.getboolean('OAuth', 'show_selection_alert', fallback=False) @@ -538,43 +548,49 @@ class OAuthHandler: alert('{alert_message}'); """) except: - pass # Alert is optional + logger.warning("Failed to display alert") # Enhanced logging + pass - # Wait for authentication to complete auth_info = self._wait_for_auth() if not auth_info: + logger.error("Authentication timeout") # Enhanced logging print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}") return False, None + logger.info("Google authentication successful") # Enhanced logging 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: + logger.error(f"Authentication error: {str(e)}", exc_info=True) # Enhanced logging 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() + logger.info("Browser closed") # Enhanced logging except: + logger.warning("Failed to close browser") # Enhanced logging pass except Exception as e: + logger.error(f"Google OAuth failed: {str(e)}", exc_info=True) # Enhanced logging 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 + max_wait = 300 start_time = time.time() - check_interval = 2 # Check every 2 seconds + check_interval = 2 + logger.info("Waiting for authentication (timeout: 5 minutes)") # Enhanced logging 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: @@ -582,7 +598,7 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) if token: - # Get email from settings page + logger.info("Authentication successful, getting account info") # Enhanced logging 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) @@ -592,15 +608,17 @@ class OAuthHandler: 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 + logger.info(f"Found email: {email}") # Enhanced logging 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 + logger.warning("Could not find email, using fallback") # Enhanced logging + 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 + logger.info(f"Usage count: {usage_text}") # Enhanced logging 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): @@ -615,136 +633,165 @@ class OAuthHandler: return False if check_usage_limits(usage_text): + logger.info("Account has reached maximum usage, deleting") # Enhanced logging 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(): + logger.info("Starting new authentication process") # Enhanced logging 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: + logger.error("Failed to delete expired account") # Enhanced logging 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: + logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging 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: + logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging 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: + logger.info("Detected successful login via URL") # Enhanced logging 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: + logger.warning(f"Error during auth wait: {str(e)}") # Enhanced logging 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) + logger.error("Authentication timeout") # Enhanced logging 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: + logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True) # Enhanced logging 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: + logger.info("Starting GitHub OAuth authentication") # Enhanced logging 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', error=str(e)) if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}") + logger.error("Browser failed to initialize") # Enhanced logging + 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 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: + # Retry navigation up to 3 times + max_retries = 3 + for attempt in range(max_retries): try: - if self.browser: - self.browser.quit() - except: - pass + logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page") # Enhanced logging + 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')) + logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging + + # Updated selectors for GitHub button + selectors = [ + "//a[contains(@href,'GitHubOAuth')]", # Primary selector + "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]", # Class-based + "(//a[contains(@class,'auth-method-button')])[2]", # Second button fallback + "//a[contains(text(),'GitHub')]", # Text-based fallback + "//a[@href='/auth/github']" # Additional fallback + ] + + auth_btn = None + for selector in selectors: + logger.debug(f"Trying selector: {selector}") # Enhanced logging + try: + auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout + if auth_btn and auth_btn.is_displayed(): + logger.info(f"Found GitHub auth button with selector: {selector}") # Enhanced logging + break + except Exception as e: + logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging + continue + + if not auth_btn: + logger.error("Could not find GitHub authentication button") # Enhanced logging + raise Exception("Could not find GitHub authentication button") + + logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging + print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}") + try: + auth_btn.click() + except Exception as e: + logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging + self.browser.run_js("arguments[0].click();", auth_btn) + time.sleep(get_random_wait_time(self.config, 'page_load_wait')) + logger.info(f"Current URL after clicking GitHub button: {self.browser.url}") # Enhanced logging + + auth_info = self._wait_for_auth() + if not auth_info: + logger.error("Authentication timeout") # Enhanced logging + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}") + return False, None + + logger.info("GitHub authentication successful") # Enhanced logging + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}") + return True, auth_info + + except Exception as e: + logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True) # Enhanced logging + 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}") + if attempt < max_retries - 1: + logger.info("Retrying navigation") # Enhanced logging + time.sleep(2) + continue + return False, None + finally: + try: + if self.browser and attempt == max_retries - 1: + self.browser.quit() + logger.info("Browser closed") # Enhanced logging + except: + logger.warning("Failed to close browser") # Enhanced logging + pass except Exception as e: + logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True) # Enhanced logging 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') - """ + """Handle OAuth authentication for both Google and 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')) + logger.info(f"Current URL in _handle_oauth: {self.browser.url}") # Enhanced logging - # 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 + else: 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 + max_button_wait = 30 button_start_time = time.time() while time.time() - button_start_time < max_button_wait: for selector in selectors: + logger.debug(f"Trying selector in _handle_oauth: {selector}") # Enhanced logging try: auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1) if auth_btn and auth_btn.is_displayed(): + logger.info(f"Found auth button in _handle_oauth: {selector}") # Enhanced logging break except: continue @@ -753,11 +800,10 @@ class OAuthHandler: time.sleep(1) if auth_btn: - # Click the button and wait for page load + logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging 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: @@ -765,13 +811,13 @@ class OAuthHandler: alert('{alert_message}'); """) except Exception as e: + logger.warning(f"Alert display failed: {str(e)}") # Enhanced logging 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 + max_wait = 300 start_time = time.time() last_url = self.browser.url @@ -779,7 +825,6 @@ class OAuthHandler: while time.time() - start_time < max_wait: try: - # Check for authentication cookies cookies = self.browser.cookies() for cookie in cookies: @@ -787,27 +832,28 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) if token: + logger.info("Authentication successful in _handle_oauth") # Enhanced logging 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 + time.sleep(3) - # 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 + logger.info(f"Found email: {actual_email}") # Enhanced logging 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: + logger.warning(f"Could not find email: {str(e)}") # Enhanced logging 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 + logger.info(f"Usage count: {usage_text}") # Enhanced logging 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): @@ -822,26 +868,30 @@ class OAuthHandler: return False if check_usage_limits(usage_text): + logger.info("Account has reached maximum usage, deleting") # Enhanced logging 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(): + logger.info("Starting new authentication process") # Enhanced logging 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: + logger.error("Failed to delete expired account") # Enhanced logging 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: + logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging 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: + logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging 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: + logger.info("Already on settings page") # Enhanced logging 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() @@ -850,21 +900,22 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) 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 + logger.info(f"Found email: {actual_email}") # Enhanced logging 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: + logger.warning(f"Could not find email: {str(e)}") # Enhanced logging 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 + logger.info(f"Usage count: {usage_text}") # Enhanced logging 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): @@ -879,49 +930,58 @@ class OAuthHandler: return False if check_usage_limits(usage_text): + logger.info("Account has reached maximum usage, deleting") # Enhanced logging 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(): + logger.info("Starting new authentication process") # Enhanced logging 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: + logger.error("Failed to delete expired account") # Enhanced logging 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: + logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging 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: + logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging 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: + logger.info(f"Page changed to {current_url}") # Enhanced logging 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: + logger.warning(f"Status check error: {str(e)}") # Enhanced logging 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) + logger.error("Authentication timeout") # Enhanced logging print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}") return False, None + logger.error("Authentication button not found") # Enhanced logging 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: + logger.error(f"Authentication failed: {str(e)}", exc_info=True) # Enhanced logging 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() + logger.info("Browser closed in _handle_oauth") # Enhanced logging 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: @@ -934,7 +994,7 @@ class OAuthHandler: raise time.sleep(1) - # Debug cookie information + logger.info(f"Found {len(cookies)} cookies") # Enhanced logging 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 @@ -947,12 +1007,14 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) except Exception as e: + logger.error(f"Failed to extract token: {str(e)}") # Enhanced logging error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") elif name == "cursor_email": email = cookie.get("value") if email and token: + logger.info(f"Authentication successful - Email: {email}") # Enhanced logging 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: @@ -961,11 +1023,13 @@ class OAuthHandler: missing.append("email") if not token: missing.append("token") + logger.error(f"Missing authentication data: {', '.join(missing)}") # Enhanced logging error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False, None except Exception as e: + logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True) # Enhanced logging error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False, None @@ -999,9 +1063,9 @@ class OAuthHandler: """ result = self.browser.run_js(delete_js) + logger.info(f"Delete account result: {result}") # Enhanced logging 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')) @@ -1009,17 +1073,14 @@ class OAuthHandler: return True except Exception as e: + logger.error(f"Failed to delete account: {str(e)}", exc_info=True) # Enhanced logging error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{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 - """ + """Main function to handle OAuth authentication""" + logger.info(f"Starting OAuth main with auth_type: {auth_type}") # Enhanced logging handler = OAuthHandler(translator, auth_type) if auth_type.lower() == 'google': @@ -1029,24 +1090,26 @@ def main(auth_type, translator=None): 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: + logger.error("Invalid authentication type") # Enhanced logging 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"] ): + logger.info("Auth update success") # Enhanced logging 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() + logger.info("Browser closed") # Enhanced logging print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}") return True else: + logger.error("Auth update failed") # Enhanced logging print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}") - return False \ No newline at end of file + return False \ No newline at end of file From 61530416074f23f4abc595e3435495ceb14d39c2 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 23:22:56 +0800 Subject: [PATCH 27/30] fix: handle --- oauth_auth.py | 291 ++++++++++++++++++++++---------------------------- 1 file changed, 126 insertions(+), 165 deletions(-) diff --git a/oauth_auth.py b/oauth_auth.py index 0c518a3..b15c71a 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -6,7 +6,7 @@ import random import webbrowser import sys import json -import logging # Added for detailed logging +import logging from DrissionPage import ChromiumPage, ChromiumOptions from cursor_auth import CursorAuth from utils import get_random_wait_time, get_default_browser_path @@ -72,14 +72,13 @@ class OAuthHandler: profiles.append((item, profile_names.get(item, item))) return sorted(profiles) except Exception as e: - logger.error(f"Error loading Chrome profiles: {str(e)}") # Enhanced logging + logger.error(f"Error loading Chrome profiles: {str(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): """Allow user to select a browser profile to use""" try: - # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type_display = browser_type.capitalize() @@ -91,10 +90,8 @@ class OAuthHandler: print(f"{Fore.CYAN}{EMOJI['INFO']} Select {browser_type_display} profile to use:{Style.RESET_ALL}") print(f"Available {browser_type_display} profiles:") - # Get the user data directory for the browser type user_data_dir = self._get_user_data_directory() - # Load available profiles from the selected browser type try: local_state_file = os.path.join(user_data_dir, "Local State") if os.path.exists(local_state_file): @@ -153,13 +150,13 @@ class OAuthHandler: return True except Exception as e: - logger.error(f"Error loading profiles: {str(e)}") # Enhanced logging + logger.error(f"Error loading profiles: {str(e)}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}") self.selected_profile = "Default" return True except Exception as e: - logger.error(f"Profile selection error: {str(e)}") # Enhanced logging + logger.error(f"Profile selection error: {str(e)}") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}") self.selected_profile = "Default" return True @@ -167,11 +164,11 @@ class OAuthHandler: def setup_browser(self): """Setup browser for OAuth flow using selected profile""" try: - logger.info("Initializing browser setup") # Enhanced logging + logger.info("Initializing browser setup") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.initializing_browser_setup') if self.translator else 'Initializing browser setup...'}{Style.RESET_ALL}") platform_name = platform.system().lower() - logger.info(f"Detected platform: {platform_name}") # Enhanced logging + logger.info(f"Detected platform: {platform_name}") 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}") config = get_config(self.translator) @@ -189,10 +186,10 @@ class OAuthHandler: "- macOS: Google Chrome, Chromium\n" + "- Linux: Google Chrome, Chromium, google-chrome-stable" ) - logger.error("No compatible browser found") # Enhanced logging + logger.error("No compatible browser found") raise Exception(error_msg) - logger.info(f"Found browser data directory: {user_data_dir}") # Enhanced logging + logger.info(f"Found browser data directory: {user_data_dir}") 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}") if self.translator: @@ -204,33 +201,33 @@ class OAuthHandler: choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower() if choice != 'y': - logger.info("Operation cancelled by user") # Enhanced logging + logger.info("Operation cancelled by user") 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 self._kill_browser_processes() if not self._select_profile(): - logger.info("Operation cancelled by user during profile selection") # Enhanced logging + logger.info("Operation cancelled by user during profile selection") 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 co = self._configure_browser_options(browser_path, user_data_dir, self.selected_profile) - logger.info(f"Starting browser at: {browser_path}") # Enhanced logging + logger.info(f"Starting browser at: {browser_path}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_path}'}{Style.RESET_ALL}") self.browser = ChromiumPage(co) if not self.browser: - logger.error("Failed to initialize browser instance") # Enhanced logging + logger.error("Failed to initialize browser instance") raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}") - logger.info("Browser setup completed successfully") # Enhanced logging + logger.info("Browser setup completed successfully") 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: - logger.error(f"Browser setup failed: {str(e)}", exc_info=True) # Enhanced logging with stack trace + logger.error(f"Browser setup failed: {str(e)}", exc_info=True) 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}") @@ -284,7 +281,7 @@ class OAuthHandler: processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, []) - logger.info(f"Killing {browser_type} processes: {processes}") # Enhanced logging + logger.info(f"Killing {browser_type} processes: {processes}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}") if os.name == 'nt': @@ -296,7 +293,7 @@ class OAuthHandler: time.sleep(1) except Exception as e: - logger.warning(f"Could not kill browser processes: {str(e)}") # Enhanced logging + logger.warning(f"Could not kill browser processes: {str(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): @@ -337,16 +334,16 @@ class OAuthHandler: user_data_dir = user_data_dirs.get(browser_type) if user_data_dir and os.path.exists(user_data_dir): - logger.info(f"Found {browser_type} user data directory: {user_data_dir}") # Enhanced logging + logger.info(f"Found {browser_type} user data directory: {user_data_dir}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}") return user_data_dir else: - logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome") # Enhanced logging + logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome") print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}") return user_data_dirs['chrome'] except Exception as e: - logger.error(f"Error getting user data directory: {str(e)}") # Enhanced logging + logger.error(f"Error getting user data directory: {str(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}") if os.name == 'nt': return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data') @@ -364,7 +361,7 @@ class OAuthHandler: browser_path = config.get('Browser', f'{browser_type}_path', fallback=None) if browser_path and os.path.exists(browser_path): - logger.info(f"Using configured {browser_type} path: {browser_path}") # Enhanced logging + logger.info(f"Using configured {browser_type} path: {browser_path}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}") return browser_path @@ -372,7 +369,7 @@ class OAuthHandler: if browser_path and os.path.exists(browser_path): return browser_path - logger.info("Searching for alternative browser installations") # Enhanced logging + logger.info("Searching for alternative browser installations") 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}") if os.name == 'nt': @@ -400,8 +397,7 @@ class OAuthHandler: os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'), os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe'), os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'), - os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Oper -a GX', 'opera.exe') + os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe') ] else: possible_paths = [ @@ -439,19 +435,19 @@ a GX', 'opera.exe') for path in possible_paths: if os.path.exists(path): - logger.info(f"Found browser at: {path}") # Enhanced logging + logger.info(f"Found browser at: {path}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}") return path if browser_type != 'chrome': - logger.warning(f"Could not find {browser_type}, trying Chrome") # Enhanced logging + logger.warning(f"Could not find {browser_type}, trying Chrome") print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}") return self._get_chrome_path() return None except Exception as e: - logger.error(f"Error finding browser path: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Error finding browser path: {str(e)}", exc_info=True) 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 @@ -477,31 +473,31 @@ a GX', 'opera.exe') co.set_argument('--disable-features=TranslateUI') co.set_argument('--disable-features=RendererCodeIntegrity') - logger.info("Browser options configured successfully") # Enhanced logging + logger.info("Browser options configured successfully") return co except Exception as e: - logger.error(f"Error configuring browser options: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Error configuring browser options: {str(e)}", exc_info=True) 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: - logger.info("Starting Google OAuth authentication") # Enhanced logging + logger.info("Starting Google OAuth authentication") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.google_start') if self.translator else 'Starting Google OAuth authentication...'}{Style.RESET_ALL}") if not self.setup_browser(): - logger.error("Browser failed to initialize") # Enhanced logging + logger.error("Browser failed to initialize") 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 try: - logger.info("Navigating to authentication page") # Enhanced logging + logger.info("Navigating to authentication page") 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')) - logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging + logger.info(f"Current URL after navigation: {self.browser.url}") selectors = [ "//a[contains(@href,'GoogleOAuth')]", @@ -511,31 +507,31 @@ a GX', 'opera.exe') auth_btn = None for selector in selectors: - logger.debug(f"Trying selector: {selector}") # Enhanced logging + logger.debug(f"Trying selector: {selector}") try: - auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout + auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) if auth_btn and auth_btn.is_displayed(): - logger.info(f"Found Google auth button with selector: {selector}") # Enhanced logging + logger.info(f"Found Google auth button with selector: {selector}") break except Exception as e: - logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging + logger.warning(f"Selector {selector} failed: {str(e)}") continue if not auth_btn: - logger.error("Could not find Google authentication button") # Enhanced logging + logger.error("Could not find Google authentication button") raise Exception("Could not find Google authentication button") - logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging + logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_google_authentication') if self.translator else 'Starting Google authentication...'}{Style.RESET_ALL}") try: auth_btn.click() except Exception as e: - logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging + logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") self.browser.run_js("arguments[0].click();", auth_btn) time.sleep(get_random_wait_time(self.config, 'page_load_wait')) if "accounts.google.com" in self.browser.url: - logger.info("On Google account selection page") # Enhanced logging + logger.info("On Google account selection page") 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}") config = get_config(self.translator) @@ -548,34 +544,34 @@ a GX', 'opera.exe') alert('{alert_message}'); """) except: - logger.warning("Failed to display alert") # Enhanced logging + logger.warning("Failed to display alert") pass auth_info = self._wait_for_auth() if not auth_info: - logger.error("Authentication timeout") # Enhanced logging + logger.error("Authentication timeout") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}") return False, None - logger.info("Google authentication successful") # Enhanced logging + logger.info("Google authentication successful") 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: - logger.error(f"Authentication error: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Authentication error: {str(e)}", exc_info=True) 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() - logger.info("Browser closed") # Enhanced logging + logger.info("Browser closed") except: - logger.warning("Failed to close browser") # Enhanced logging + logger.warning("Failed to close browser") pass except Exception as e: - logger.error(f"Google OAuth failed: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Google OAuth failed: {str(e)}", exc_info=True) print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}") return False, None @@ -586,7 +582,7 @@ a GX', 'opera.exe') start_time = time.time() check_interval = 2 - logger.info("Waiting for authentication (timeout: 5 minutes)") # Enhanced logging + logger.info("Waiting for authentication (timeout: 5 minutes)") 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: @@ -598,7 +594,7 @@ a GX', 'opera.exe') value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) if token: - logger.info("Authentication successful, getting account info") # Enhanced logging + logger.info("Authentication successful, getting account info") 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) @@ -608,17 +604,17 @@ a GX', 'opera.exe') 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 - logger.info(f"Found email: {email}") # Enhanced logging + logger.info(f"Found email: {email}") 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: - logger.warning("Could not find email, using fallback") # Enhanced logging + logger.warning("Could not find email, using fallback") email = "user@cursor.sh" 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 - logger.info(f"Usage count: {usage_text}") # Enhanced logging + logger.info(f"Usage count: {usage_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): @@ -633,117 +629,115 @@ a GX', 'opera.exe') return False if check_usage_limits(usage_text): - logger.info("Account has reached maximum usage, deleting") # Enhanced logging + logger.info("Account has reached maximum usage, deleting") 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(): - logger.info("Starting new authentication process") # Enhanced logging + logger.info("Starting new authentication process") 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: - logger.error("Failed to delete expired account") # Enhanced logging + logger.error("Failed to delete expired account") 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: - logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging + logger.info(f"Account is still valid (Usage: {usage_text})") 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: - logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging + logger.warning(f"Could not check usage count: {str(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} if "cursor.com/settings" in self.browser.url: - logger.info("Detected successful login via URL") # Enhanced logging + logger.info("Detected successful login via 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: - logger.warning(f"Error during auth wait: {str(e)}") # Enhanced logging + logger.warning(f"Error during auth wait: {str(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) - logger.error("Authentication timeout") # Enhanced logging + logger.error("Authentication timeout") 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: - logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True) 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: - logger.info("Starting GitHub OAuth authentication") # Enhanced logging + logger.info("Starting GitHub OAuth authentication") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}") if not self.setup_browser(): - logger.error("Browser failed to initialize") # Enhanced logging + logger.error("Browser failed to initialize") 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 - # Retry navigation up to 3 times max_retries = 3 for attempt in range(max_retries): try: - logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page") # Enhanced logging + logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page") 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')) - logger.info(f"Current URL after navigation: {self.browser.url}") # Enhanced logging + logger.info(f"Current URL after navigation: {self.browser.url}") - # Updated selectors for GitHub button selectors = [ - "//a[contains(@href,'GitHubOAuth')]", # Primary selector - "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]", # Class-based - "(//a[contains(@class,'auth-method-button')])[2]", # Second button fallback - "//a[contains(text(),'GitHub')]", # Text-based fallback - "//a[@href='/auth/github']" # Additional fallback + "//a[contains(@href,'GitHubOAuth')]", + "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]", + "(//a[contains(@class,'auth-method-button')])[2]", + "//a[contains(text(),'GitHub')]", + "//a[@href='/auth/github']" ] auth_btn = None for selector in selectors: - logger.debug(f"Trying selector: {selector}") # Enhanced logging + logger.debug(f"Trying selector: {selector}") try: - auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) # Increased timeout + auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) if auth_btn and auth_btn.is_displayed(): - logger.info(f"Found GitHub auth button with selector: {selector}") # Enhanced logging + logger.info(f"Found GitHub auth button with selector: {selector}") break except Exception as e: - logger.warning(f"Selector {selector} failed: {str(e)}") # Enhanced logging + logger.warning(f"Selector {selector} failed: {str(e)}") continue if not auth_btn: - logger.error("Could not find GitHub authentication button") # Enhanced logging + logger.error("Could not find GitHub authentication button") raise Exception("Could not find GitHub authentication button") - logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging + logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}") try: auth_btn.click() except Exception as e: - logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") # Enhanced logging + logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") self.browser.run_js("arguments[0].click();", auth_btn) time.sleep(get_random_wait_time(self.config, 'page_load_wait')) - logger.info(f"Current URL after clicking GitHub button: {self.browser.url}") # Enhanced logging + logger.info(f"Current URL after clicking GitHub button: {self.browser.url}") auth_info = self._wait_for_auth() if not auth_info: - logger.error("Authentication timeout") # Enhanced logging + logger.error("Authentication timeout") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}") return False, None - logger.info("GitHub authentication successful") # Enhanced logging + logger.info("GitHub authentication successful") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}") return True, auth_info except Exception as e: - logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True) 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}") if attempt < max_retries - 1: - logger.info("Retrying navigation") # Enhanced logging + logger.info("Retrying navigation") time.sleep(2) continue return False, None @@ -751,13 +745,13 @@ a GX', 'opera.exe') try: if self.browser and attempt == max_retries - 1: self.browser.quit() - logger.info("Browser closed") # Enhanced logging + logger.info("Browser closed") except: - logger.warning("Failed to close browser") # Enhanced logging + logger.warning("Failed to close browser") pass except Exception as e: - logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True) print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e))}{Style.RESET_ALL}") return False, None @@ -769,7 +763,7 @@ a GX', 'opera.exe') self.browser.get("https://authenticator.cursor.sh/sign-up") time.sleep(get_random_wait_time(self.config, 'page_load_wait')) - logger.info(f"Current URL in _handle_oauth: {self.browser.url}") # Enhanced logging + logger.info(f"Current URL in _handle_oauth: {self.browser.url}") if auth_type == "google": selectors = [ @@ -787,11 +781,11 @@ a GX', 'opera.exe') while time.time() - button_start_time < max_button_wait: for selector in selectors: - logger.debug(f"Trying selector in _handle_oauth: {selector}") # Enhanced logging + logger.debug(f"Trying selector in _handle_oauth: {selector}") try: auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1) if auth_btn and auth_btn.is_displayed(): - logger.info(f"Found auth button in _handle_oauth: {selector}") # Enhanced logging + logger.info(f"Found auth button in _handle_oauth: {selector}") break except: continue @@ -800,7 +794,7 @@ a GX', 'opera.exe') time.sleep(1) if auth_btn: - logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") # Enhanced logging + logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") auth_btn.click() time.sleep(get_random_wait_time(self.config, 'page_load_wait')) @@ -811,7 +805,7 @@ a GX', 'opera.exe') alert('{alert_message}'); """) except Exception as e: - logger.warning(f"Alert display failed: {str(e)}") # Enhanced logging + logger.warning(f"Alert display failed: {str(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}") @@ -832,7 +826,7 @@ a GX', 'opera.exe') value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) if token: - logger.info("Authentication successful in _handle_oauth") # Enhanced logging + logger.info("Authentication successful in _handle_oauth") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}") 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") @@ -842,10 +836,10 @@ a GX', 'opera.exe') 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 - logger.info(f"Found email: {actual_email}") # Enhanced logging + logger.info(f"Found email: {actual_email}") 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: - logger.warning(f"Could not find email: {str(e)}") # Enhanced logging + logger.warning(f"Could not find email: {str(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" @@ -853,7 +847,7 @@ a GX', 'opera.exe') 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 - logger.info(f"Usage count: {usage_text}") # Enhanced logging + logger.info(f"Usage count: {usage_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): @@ -868,30 +862,30 @@ a GX', 'opera.exe') return False if check_usage_limits(usage_text): - logger.info("Account has reached maximum usage, deleting") # Enhanced logging + logger.info("Account has reached maximum usage, deleting") 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(): - logger.info("Starting new authentication process") # Enhanced logging + logger.info("Starting new authentication process") 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: - logger.error("Failed to delete expired account") # Enhanced logging + logger.error("Failed to delete expired account") 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: - logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging + logger.info(f"Account is still valid (Usage: {usage_text})") 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: - logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging + logger.warning(f"Could not check usage count: {str(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 True, {"email": actual_email, "token": token} current_url = self.browser.url if "cursor.com/settings" in current_url: - logger.info("Already on settings page") # Enhanced logging + logger.info("Already on settings page") 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() @@ -904,10 +898,10 @@ a GX', 'opera.exe') 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 - logger.info(f"Found email: {actual_email}") # Enhanced logging + logger.info(f"Found email: {actual_email}") 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: - logger.warning(f"Could not find email: {str(e)}") # Enhanced logging + logger.warning(f"Could not find email: {str(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" @@ -915,7 +909,7 @@ a GX', 'opera.exe') 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 - logger.info(f"Usage count: {usage_text}") # Enhanced logging + logger.info(f"Usage count: {usage_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): @@ -930,54 +924,54 @@ a GX', 'opera.exe') return False if check_usage_limits(usage_text): - logger.info("Account has reached maximum usage, deleting") # Enhanced logging + logger.info("Account has reached maximum usage, deleting") 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(): - logger.info("Starting new authentication process") # Enhanced logging + logger.info("Starting new authentication process") 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: - logger.error("Failed to delete expired account") # Enhanced logging + logger.error("Failed to delete expired account") 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: - logger.info(f"Account is still valid (Usage: {usage_text})") # Enhanced logging + logger.info(f"Account is still valid (Usage: {usage_text})") 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: - logger.warning(f"Could not check usage count: {str(e)}") # Enhanced logging + logger.warning(f"Could not check usage count: {str(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 True, {"email": actual_email, "token": token} elif current_url != last_url: - logger.info(f"Page changed to {current_url}") # Enhanced logging + logger.info(f"Page changed to {current_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: - logger.warning(f"Status check error: {str(e)}") # Enhanced logging + logger.warning(f"Status check error: {str(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) - logger.error("Authentication timeout") # Enhanced logging + logger.error("Authentication timeout") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}") return False, None - logger.error("Authentication button not found") # Enhanced logging + logger.error("Authentication button not found") 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: - logger.error(f"Authentication failed: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Authentication failed: {str(e)}", exc_info=True) 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() - logger.info("Browser closed in _handle_oauth") # Enhanced logging + logger.info("Browser closed in _handle_oauth") def _extract_auth_info(self): """Extract authentication information after successful OAuth""" @@ -994,7 +988,7 @@ a GX', 'opera.exe') raise time.sleep(1) - logger.info(f"Found {len(cookies)} cookies") # Enhanced logging + logger.info(f"Found {len(cookies)} cookies") 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 @@ -1007,14 +1001,14 @@ a GX', 'opera.exe') value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) except Exception as e: - logger.error(f"Failed to extract token: {str(e)}") # Enhanced logging + logger.error(f"Failed to extract token: {str(e)}") error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") elif name == "cursor_email": email = cookie.get("value") if email and token: - logger.info(f"Authentication successful - Email: {email}") # Enhanced logging + logger.info(f"Authentication successful - Email: {email}") 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: @@ -1023,13 +1017,13 @@ a GX', 'opera.exe') missing.append("email") if not token: missing.append("token") - logger.error(f"Missing authentication data: {', '.join(missing)}") # Enhanced logging + logger.error(f"Missing authentication data: {', '.join(missing)}") error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False, None except Exception as e: - logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True) # Enhanced logging + logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True) error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False, None @@ -1062,54 +1056,21 @@ a GX', 'opera.exe') return deleteAccount(); """ + logger.info("Attempting to delete account via API") result = self.browser.run_js(delete_js) - logger.info(f"Delete account result: {result}") # Enhanced logging - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}") + logger.info(f"Delete account result: {result}") + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.delete_account_success', result=result) if self.translator else f'Delete account result: {result}'}{Style.RESET_ALL}") + logger.info("Redirecting to authenticator.cursor.sh") 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')) + logger.info(f"Navigated to sign-up page: {self.browser.url}") return True except Exception as e: - logger.error(f"Failed to delete account: {str(e)}", exc_info=True) # Enhanced logging - error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e)) + logger.error(f"Failed to delete account: {str(e)}", exc_info=True) + error_message = self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}' print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") - return False - -def main(auth_type, translator=None): - """Main function to handle OAuth authentication""" - logger.info(f"Starting OAuth main with auth_type: {auth_type}") # Enhanced logging - 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: - logger.error("Invalid authentication type") # Enhanced logging - 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: - auth_manager = CursorAuth(translator) - if auth_manager.update_auth( - email=auth_info["email"], - access_token=auth_info["token"], - refresh_token=auth_info["token"] - ): - logger.info("Auth update success") # Enhanced logging - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('oauth.auth_update_success') if translator else 'Auth update success'}{Style.RESET_ALL}") - if handler.browser: - handler.browser.quit() - logger.info("Browser closed") # Enhanced logging - print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('oauth.browser_closed') if translator else 'Browser closed'}{Style.RESET_ALL}") - return True - else: - logger.error("Auth update failed") # Enhanced logging - print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('oauth.auth_update_failed') if translator else 'Auth update failed'}{Style.RESET_ALL}") - - return False \ No newline at end of file + return False \ No newline at end of file From 3de2db74b2d7c38357986dba31670eebd8e0608b Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 23:32:49 +0800 Subject: [PATCH 28/30] fix: unexpected --- oauth_auth.py | 404 ++++++++++++++++++++++++-------------------------- 1 file changed, 190 insertions(+), 214 deletions(-) diff --git a/oauth_auth.py b/oauth_auth.py index b15c71a..4313041 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -6,7 +6,6 @@ import random import webbrowser import sys import json -import logging from DrissionPage import ChromiumPage, ChromiumOptions from cursor_auth import CursorAuth from utils import get_random_wait_time, get_default_browser_path @@ -17,16 +16,6 @@ from get_user_token import get_token_from_cookie # Initialize colorama init() -# Set up logging -logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s - %(levelname)s - %(message)s', - handlers=[ - logging.StreamHandler(sys.stdout) - ] -) -logger = logging.getLogger(__name__) - # Define emoji constants EMOJI = { 'START': '🚀', @@ -72,26 +61,29 @@ class OAuthHandler: profiles.append((item, profile_names.get(item, item))) return sorted(profiles) except Exception as e: - logger.error(f"Error loading Chrome profiles: {str(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): """Allow user to select a browser profile to use""" try: + # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type_display = browser_type.capitalize() if self.translator: + # 动态使用浏览器类型 print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('browser_profile.select_profile', browser=browser_type_display)}{Style.RESET_ALL}") print(f"{Fore.CYAN}{self.translator.get('browser_profile.profile_list', browser=browser_type_display)}{Style.RESET_ALL}") else: print(f"{Fore.CYAN}{EMOJI['INFO']} Select {browser_type_display} profile to use:{Style.RESET_ALL}") print(f"Available {browser_type_display} profiles:") + # Get the user data directory for the browser type user_data_dir = self._get_user_data_directory() + # Load available profiles from the selected browser type try: local_state_file = os.path.join(user_data_dir, "Local State") if os.path.exists(local_state_file): @@ -99,15 +91,19 @@ class OAuthHandler: state_data = json.load(f) profiles_data = state_data.get('profile', {}).get('info_cache', {}) + # Create a list of available profiles profiles = [] for profile_id, profile_info in profiles_data.items(): name = profile_info.get('name', profile_id) + # Mark the default profile if profile_id.lower() == 'default': name = f"{name} (Default)" profiles.append((profile_id, name)) + # Sort profiles by name profiles.sort(key=lambda x: x[1]) + # Show available profiles if self.translator: print(f"{Fore.CYAN}0. {self.translator.get('menu.exit')}{Style.RESET_ALL}") else: @@ -116,6 +112,7 @@ class OAuthHandler: for i, (profile_id, name) in enumerate(profiles, 1): print(f"{Fore.CYAN}{i}. {name}{Style.RESET_ALL}") + # Get user's choice max_choice = len(profiles) choice_str = input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{max_choice}') if self.translator else f'Please enter your choice (0-{max_choice})'}{Style.RESET_ALL}") @@ -145,18 +142,19 @@ class OAuthHandler: print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}") return self._select_profile() else: + # No Local State file, use Default profile print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('browser_profile.no_profiles', browser=browser_type_display) if self.translator else f'No {browser_type_display} profiles found'}{Style.RESET_ALL}") self.selected_profile = "Default" return True except Exception as e: - logger.error(f"Error loading profiles: {str(e)}") + # Error loading profiles, use Default profile print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.error_loading', error=str(e), browser=browser_type_display) if self.translator else f'Error loading {browser_type_display} profiles: {str(e)}'}{Style.RESET_ALL}") self.selected_profile = "Default" return True except Exception as e: - logger.error(f"Profile selection error: {str(e)}") + # General error, use Default profile print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.profile_selection_error', error=str(e)) if self.translator else f'Error during profile selection: {str(e)}'}{Style.RESET_ALL}") self.selected_profile = "Default" return True @@ -164,16 +162,17 @@ class OAuthHandler: def setup_browser(self): """Setup browser for OAuth flow using selected profile""" try: - logger.info("Initializing browser setup") 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() - logger.info(f"Detected platform: {platform_name}") 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}") + # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') + # Get browser paths and user data directory user_data_dir = self._get_user_data_directory() browser_path = self._get_browser_path() @@ -186,12 +185,11 @@ class OAuthHandler: "- macOS: Google Chrome, Chromium\n" + "- Linux: Google Chrome, Chromium, google-chrome-stable" ) - logger.error("No compatible browser found") raise Exception(error_msg) - logger.info(f"Found browser data directory: {user_data_dir}") 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 browser first - 使用动态提示 if self.translator: warning_msg = self.translator.get('oauth.warning_browser_close', browser=browser_type) else: @@ -201,33 +199,31 @@ class OAuthHandler: choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower() if choice != 'y': - logger.info("Operation cancelled by user") 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(): - logger.info("Operation cancelled by user during profile selection") 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(browser_path, user_data_dir, self.selected_profile) - logger.info(f"Starting browser at: {browser_path}") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=browser_path) if self.translator else f'Starting browser at: {browser_path}'}{Style.RESET_ALL}") self.browser = ChromiumPage(co) + # Verify browser launched successfully if not self.browser: - logger.error("Failed to initialize browser instance") - raise Exception(f"{self.translator.get('oauth.browser_failed_to_start') if self.translator else 'Failed to initialize browser instance'}") + raise Exception(f"{self.translator.get('oauth.browser_failed_to_start', error=str(e)) if self.translator else 'Failed to initialize browser instance'}") - logger.info("Browser setup completed successfully") 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: - logger.error(f"Browser setup failed: {str(e)}", exc_info=True) 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}") @@ -240,10 +236,12 @@ class OAuthHandler: def _kill_browser_processes(self): """Kill existing browser processes based on platform and browser type""" try: + # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type = browser_type.lower() + # 根据浏览器类型和平台定义要关闭的进程 browser_processes = { 'chrome': { 'win': ['chrome.exe', 'chromium.exe'], @@ -272,6 +270,7 @@ class OAuthHandler: } } + # 获取平台类型 if os.name == 'nt': platform_type = 'win' elif sys.platform == 'darwin': @@ -279,31 +278,34 @@ class OAuthHandler: else: platform_type = 'linux' + # 获取要关闭的进程列表 processes = browser_processes.get(browser_type, browser_processes['chrome']).get(platform_type, []) - logger.info(f"Killing {browser_type} processes: {processes}") - print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}") + if self.translator: + print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.killing_browser_processes', browser=browser_type) if self.translator else f'Killing {browser_type} processes...'}{Style.RESET_ALL}") - if os.name == 'nt': + # 根据平台关闭进程 + if os.name == 'nt': # Windows for proc in processes: os.system(f'taskkill /f /im {proc} >nul 2>&1') - else: + else: # Linux/Mac for proc in processes: os.system(f'pkill -f {proc} >/dev/null 2>&1') - time.sleep(1) + time.sleep(1) # Wait for processes to close except Exception as e: - logger.warning(f"Could not kill browser processes: {str(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 default user data directory based on browser type and platform""" try: + # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type = browser_type.lower() - if os.name == 'nt': + # 根据操作系统和浏览器类型获取用户数据目录 + if os.name == 'nt': # Windows user_data_dirs = { 'chrome': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data'), 'brave': os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'User Data'), @@ -312,7 +314,7 @@ class OAuthHandler: 'opera': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera Stable'), 'operagx': os.path.join(os.environ.get('APPDATA', ''), 'Opera Software', 'Opera GX Stable') } - elif sys.platform == 'darwin': + elif sys.platform == 'darwin': # macOS user_data_dirs = { 'chrome': os.path.expanduser('~/Library/Application Support/Google/Chrome'), 'brave': os.path.expanduser('~/Library/Application Support/BraveSoftware/Brave-Browser'), @@ -321,7 +323,7 @@ class OAuthHandler: 'opera': os.path.expanduser('~/Library/Application Support/com.operasoftware.Opera'), 'operagx': os.path.expanduser('~/Library/Application Support/com.operasoftware.OperaGX') } - else: + else: # Linux user_data_dirs = { 'chrome': os.path.expanduser('~/.config/google-chrome'), 'brave': os.path.expanduser('~/.config/BraveSoftware/Brave-Browser'), @@ -331,20 +333,19 @@ class OAuthHandler: 'operagx': os.path.expanduser('~/.config/opera-gx') } + # 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的 user_data_dir = user_data_dirs.get(browser_type) if user_data_dir and os.path.exists(user_data_dir): - logger.info(f"Found {browser_type} user data directory: {user_data_dir}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_user_data_dir', browser=browser_type, path=user_data_dir) if self.translator else f'Found {browser_type} user data directory: {user_data_dir}'}{Style.RESET_ALL}") return user_data_dir else: - logger.warning(f"{browser_type} user data directory not found at {user_data_dir}, trying Chrome") print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.user_data_dir_not_found', browser=browser_type, path=user_data_dir) if self.translator else f'{browser_type} user data directory not found at {user_data_dir}, will try Chrome instead'}{Style.RESET_ALL}") - return user_data_dirs['chrome'] + return user_data_dirs['chrome'] # 回退到 Chrome 目录 except Exception as e: - logger.error(f"Error getting user data directory: {str(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}") + # 在出错时提供一个默认目录 if os.name == 'nt': return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data') elif sys.platform == 'darwin': @@ -355,24 +356,26 @@ class OAuthHandler: def _get_browser_path(self): """Get appropriate browser path based on platform and selected browser type""" try: + # 从配置中获取浏览器类型 config = get_config(self.translator) browser_type = config.get('Browser', 'default_browser', fallback='chrome') browser_type = browser_type.lower() + # 首先检查配置中是否有明确指定的浏览器路径 browser_path = config.get('Browser', f'{browser_type}_path', fallback=None) if browser_path and os.path.exists(browser_path): - logger.info(f"Using configured {browser_type} path: {browser_path}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.using_configured_browser_path', browser=browser_type, path=browser_path) if self.translator else f'Using configured {browser_type} path: {browser_path}'}{Style.RESET_ALL}") return browser_path + # 尝试获取默认路径 browser_path = get_default_browser_path(browser_type) if browser_path and os.path.exists(browser_path): return browser_path - logger.info("Searching for alternative browser installations") 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}") - if os.name == 'nt': + # 如果未找到配置中指定的浏览器,则尝试查找其他兼容浏览器 + if os.name == 'nt': # Windows possible_paths = [] if browser_type == 'brave': possible_paths = [ @@ -399,14 +402,14 @@ class OAuthHandler: os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'), os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe') ] - else: + else: # 默认为 Chrome possible_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': + elif sys.platform == 'darwin': # macOS possible_paths = [] if browser_type == 'brave': possible_paths = ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser'] @@ -414,10 +417,10 @@ class OAuthHandler: possible_paths = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge'] elif browser_type == 'firefox': possible_paths = ['/Applications/Firefox.app/Contents/MacOS/firefox'] - else: + else: # 默认为 Chrome possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'] - else: + else: # Linux possible_paths = [] if browser_type == 'brave': possible_paths = ['/usr/bin/brave-browser', '/usr/bin/brave'] @@ -425,29 +428,28 @@ class OAuthHandler: possible_paths = ['/usr/bin/microsoft-edge'] elif browser_type == 'firefox': possible_paths = ['/usr/bin/firefox'] - else: + else: # 默认为 Chrome possible_paths = [ - '/usr/bin/google-chrome-stable', + '/usr/bin/google-chrome-stable', # 优先检查 google-chrome-stable '/usr/bin/google-chrome', '/usr/bin/chromium', '/usr/bin/chromium-browser' ] + # 检查每个可能的路径 for path in possible_paths: if os.path.exists(path): - logger.info(f"Found browser at: {path}") print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=path) if self.translator else f'Found browser at: {path}'}{Style.RESET_ALL}") return path + # 如果找不到指定浏览器,则尝试使用 Chrome if browser_type != 'chrome': - logger.warning(f"Could not find {browser_type}, trying Chrome") print(f"{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('oauth.browser_not_found_trying_chrome', browser=browser_type) if self.translator else f'Could not find {browser_type}, trying Chrome instead'}{Style.RESET_ALL}") return self._get_chrome_path() return None except Exception as e: - logger.error(f"Error finding browser path: {str(e)}", exc_info=True) 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 @@ -458,11 +460,13 @@ class OAuthHandler: co.set_paths(browser_path=browser_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') - co.set_argument('--remote-debugging-port=9222') + co.set_argument('--remote-debugging-port=9222') # 明确指定调试端口 + # Platform-specific options if sys.platform.startswith('linux'): co.set_argument('--no-sandbox') co.set_argument('--disable-dev-shm-usage') @@ -473,67 +477,57 @@ class OAuthHandler: co.set_argument('--disable-features=TranslateUI') co.set_argument('--disable-features=RendererCodeIntegrity') - logger.info("Browser options configured successfully") return co except Exception as e: - logger.error(f"Error configuring browser options: {str(e)}", exc_info=True) 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: - logger.info("Starting Google OAuth authentication") 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(): - logger.error("Browser failed to initialize") 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: - logger.info("Navigating to authentication page") 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')) - logger.info(f"Current URL after navigation: {self.browser.url}") + # 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]" + "(//a[contains(@class,'auth-method-button')])[1]" # First auth button as fallback ] auth_btn = None for selector in selectors: - logger.debug(f"Trying selector: {selector}") try: - auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) + auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2) if auth_btn and auth_btn.is_displayed(): - logger.info(f"Found Google auth button with selector: {selector}") break - except Exception as e: - logger.warning(f"Selector {selector} failed: {str(e)}") + except: continue if not auth_btn: - logger.error("Could not find Google authentication button") raise Exception("Could not find Google authentication button") - logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") + # 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}") - try: - auth_btn.click() - except Exception as e: - logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") - self.browser.run_js("arguments[0].click();", auth_btn) + 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: - logger.info("On Google account selection page") 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 选项 config = get_config(self.translator) show_alert = config.getboolean('OAuth', 'show_selection_alert', fallback=False) @@ -544,49 +538,43 @@ class OAuthHandler: alert('{alert_message}'); """) except: - logger.warning("Failed to display alert") - pass + pass # Alert is optional + # Wait for authentication to complete auth_info = self._wait_for_auth() if not auth_info: - logger.error("Authentication timeout") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}") return False, None - logger.info("Google authentication successful") 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: - logger.error(f"Authentication error: {str(e)}", exc_info=True) 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() - logger.info("Browser closed") except: - logger.warning("Failed to close browser") pass except Exception as e: - logger.error(f"Google OAuth failed: {str(e)}", exc_info=True) 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 + max_wait = 300 # 5 minutes start_time = time.time() - check_interval = 2 + check_interval = 2 # Check every 2 seconds - logger.info("Waiting for authentication (timeout: 5 minutes)") 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: @@ -594,7 +582,7 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) if token: - logger.info("Authentication successful, getting account info") + # 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) @@ -604,17 +592,15 @@ class OAuthHandler: 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 - logger.info(f"Found email: {email}") 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: - logger.warning("Could not find email, using fallback") - email = "user@cursor.sh" + 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 - logger.info(f"Usage count: {usage_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): @@ -629,163 +615,136 @@ class OAuthHandler: return False if check_usage_limits(usage_text): - logger.info("Account has reached maximum usage, deleting") 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(): - logger.info("Starting new authentication process") 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: - logger.error("Failed to delete expired account") 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: - logger.info(f"Account is still valid (Usage: {usage_text})") 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: - logger.warning(f"Could not check usage count: {str(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: - logger.info("Detected successful login via 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: - logger.warning(f"Error during auth wait: {str(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) - logger.error("Authentication timeout") 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: - logger.error(f"Error waiting for authentication: {str(e)}", exc_info=True) 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: - logger.info("Starting GitHub OAuth authentication") print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.github_start')}{Style.RESET_ALL}") + # Setup browser if not self.setup_browser(): - logger.error("Browser failed to initialize") - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed') if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}") + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed', error=str(e)) if self.translator else 'Browser failed to initialize'}{Style.RESET_ALL}") return False, None - max_retries = 3 - for attempt in range(max_retries): - try: - logger.info(f"Attempt {attempt + 1}/{max_retries}: Navigating to authentication page") - 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')) - logger.info(f"Current URL after navigation: {self.browser.url}") - - selectors = [ - "//a[contains(@href,'GitHubOAuth')]", - "//a[contains(@class,'auth-method-button') and contains(@href,'GitHubOAuth')]", - "(//a[contains(@class,'auth-method-button')])[2]", - "//a[contains(text(),'GitHub')]", - "//a[@href='/auth/github']" - ] - - auth_btn = None - for selector in selectors: - logger.debug(f"Trying selector: {selector}") - try: - auth_btn = self.browser.ele(f"xpath:{selector}", timeout=5) - if auth_btn and auth_btn.is_displayed(): - logger.info(f"Found GitHub auth button with selector: {selector}") - break - except Exception as e: - logger.warning(f"Selector {selector} failed: {str(e)}") - continue - - if not auth_btn: - logger.error("Could not find GitHub authentication button") - raise Exception("Could not find GitHub authentication button") - - logger.debug(f"Button state - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") - print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_github_authentication') if self.translator else 'Starting GitHub authentication...'}{Style.RESET_ALL}") + # 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.click() - except Exception as e: - logger.warning(f"Standard click failed: {str(e)}, attempting JavaScript click") - self.browser.run_js("arguments[0].click();", auth_btn) - time.sleep(get_random_wait_time(self.config, 'page_load_wait')) - logger.info(f"Current URL after clicking GitHub button: {self.browser.url}") - - auth_info = self._wait_for_auth() - if not auth_info: - logger.error("Authentication timeout") - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout'}{Style.RESET_ALL}") - return False, None - - logger.info("GitHub authentication successful") - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.success')}{Style.RESET_ALL}") - return True, auth_info - - except Exception as e: - logger.error(f"Attempt {attempt + 1} failed: {str(e)}", exc_info=True) - 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}") - if attempt < max_retries - 1: - logger.info("Retrying navigation") - time.sleep(2) - continue - return False, None - finally: - try: - if self.browser and attempt == max_retries - 1: - self.browser.quit() - logger.info("Browser closed") + auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2) + if auth_btn and auth_btn.is_displayed(): + break except: - logger.warning("Failed to close browser") - pass + 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: - logger.error(f"GitHub OAuth failed: {str(e)}", exc_info=True) 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""" + """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')) - logger.info(f"Current URL in _handle_oauth: {self.browser.url}") + # 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: + 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 + max_button_wait = 30 # 30 seconds button_start_time = time.time() while time.time() - button_start_time < max_button_wait: for selector in selectors: - logger.debug(f"Trying selector in _handle_oauth: {selector}") try: auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1) if auth_btn and auth_btn.is_displayed(): - logger.info(f"Found auth button in _handle_oauth: {selector}") break except: continue @@ -794,10 +753,11 @@ class OAuthHandler: time.sleep(1) if auth_btn: - logger.debug(f"Button state in _handle_oauth - displayed: {auth_btn.is_displayed()}, enabled: {auth_btn.is_enabled()}") + # 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: @@ -805,13 +765,13 @@ class OAuthHandler: alert('{alert_message}'); """) except Exception as e: - logger.warning(f"Alert display failed: {str(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}") - max_wait = 300 + # Wait for authentication to complete + max_wait = 300 # 5 minutes start_time = time.time() last_url = self.browser.url @@ -819,6 +779,7 @@ class OAuthHandler: while time.time() - start_time < max_wait: try: + # Check for authentication cookies cookies = self.browser.cookies() for cookie in cookies: @@ -826,28 +787,27 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) if token: - logger.info("Authentication successful in _handle_oauth") 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) + 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 - logger.info(f"Found email: {actual_email}") 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: - logger.warning(f"Could not find email: {str(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 - logger.info(f"Usage count: {usage_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): @@ -862,30 +822,26 @@ class OAuthHandler: return False if check_usage_limits(usage_text): - logger.info("Account has reached maximum usage, deleting") 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(): - logger.info("Starting new authentication process") 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: - logger.error("Failed to delete expired account") 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: - logger.info(f"Account is still valid (Usage: {usage_text})") 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: - logger.warning(f"Could not check usage count: {str(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: - logger.info("Already on settings page") 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() @@ -894,22 +850,21 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) 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 - logger.info(f"Found email: {actual_email}") 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: - logger.warning(f"Could not find email: {str(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 - logger.info(f"Usage count: {usage_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): @@ -924,58 +879,49 @@ class OAuthHandler: return False if check_usage_limits(usage_text): - logger.info("Account has reached maximum usage, deleting") 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(): - logger.info("Starting new authentication process") 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: - logger.error("Failed to delete expired account") 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: - logger.info(f"Account is still valid (Usage: {usage_text})") 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: - logger.warning(f"Could not check usage count: {str(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: - logger.info(f"Page changed to {current_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: - logger.warning(f"Status check error: {str(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) - logger.error("Authentication timeout") print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.authentication_timeout') if self.translator else 'Authentication timeout'}{Style.RESET_ALL}") return False, None - logger.error("Authentication button not found") 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: - logger.error(f"Authentication failed: {str(e)}", exc_info=True) 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() - logger.info("Browser closed in _handle_oauth") 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: @@ -988,7 +934,7 @@ class OAuthHandler: raise time.sleep(1) - logger.info(f"Found {len(cookies)} cookies") + # 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 @@ -1001,14 +947,12 @@ class OAuthHandler: value = cookie.get("value", "") token = get_token_from_cookie(value, self.translator) except Exception as e: - logger.error(f"Failed to extract token: {str(e)}") error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") elif name == "cursor_email": email = cookie.get("value") if email and token: - logger.info(f"Authentication successful - Email: {email}") 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: @@ -1017,13 +961,11 @@ class OAuthHandler: missing.append("email") if not token: missing.append("token") - logger.error(f"Missing authentication data: {', '.join(missing)}") error_message = f"Missing authentication data: {', '.join(missing)}" if not self.translator else self.translator.get('oauth.missing_authentication_data', data=', '.join(missing)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False, None except Exception as e: - logger.error(f"Failed to extract auth info: {str(e)}", exc_info=True) error_message = f'Failed to extract auth info: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_extract_auth_info', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") return False, None @@ -1056,21 +998,55 @@ class OAuthHandler: return deleteAccount(); """ - logger.info("Attempting to delete account via API") result = self.browser.run_js(delete_js) - logger.info(f"Delete account result: {result}") - print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.delete_account_success', result=result) if self.translator else f'Delete account result: {result}'}{Style.RESET_ALL}") + print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Delete account result: {result}{Style.RESET_ALL}") - logger.info("Redirecting to authenticator.cursor.sh") + # 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')) - logger.info(f"Navigated to sign-up page: {self.browser.url}") return True except Exception as e: - logger.error(f"Failed to delete account: {str(e)}", exc_info=True) - error_message = self.translator.get('oauth.failed_to_delete_account', error=str(e)) if self.translator else f'Failed to delete account: {str(e)}' + error_message = f'Failed to delete account: {str(e)}' if not self.translator else self.translator.get('oauth.failed_to_delete_account', error=str(e)) print(f"{Fore.RED}{EMOJI['ERROR']} {error_message}{Style.RESET_ALL}") - return False \ No newline at end of file + 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 \ No newline at end of file From f325690e32466c950a90f3dfe873dfeeb98fd5de Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Mon, 14 Apr 2025 23:48:25 +0800 Subject: [PATCH 29/30] add: more output --- oauth_auth.py | 125 +++++++------------------------------------------- 1 file changed, 16 insertions(+), 109 deletions(-) diff --git a/oauth_auth.py b/oauth_auth.py index 4313041..d4ff098 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -676,7 +676,8 @@ class OAuthHandler: auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2) if auth_btn and auth_btn.is_displayed(): break - except: + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}") continue if not auth_btn: @@ -703,13 +704,13 @@ class OAuthHandler: try: if self.browser: self.browser.quit() - except: - pass - + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.quit_browser_error', error=str(e)) if self.translator else f'Error quitting browser: {str(e)}'}{Style.RESET_ALL}") + 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 @@ -746,7 +747,8 @@ class OAuthHandler: auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1) if auth_btn and auth_btn.is_displayed(): break - except: + except Exception as e: + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}") continue if auth_btn: break @@ -808,115 +810,20 @@ class OAuthHandler: 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}") + print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_usage_count', usage=usage_text) if self.translator else f'Found usage count: {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", "") - token = get_token_from_cookie(value, self.translator) - 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" + print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.could_not_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}") - # 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')) + return True, actual_email 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}") + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_checking_cookies', error=str(e)) if self.translator else f'Error checking cookies: {str(e)}'}{Style.RESET_ALL}") + time.sleep(5) + + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout during authentication'}{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}") + print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e)) if self.translator else f'OAuth 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""" From 105b5d45178e543dcd5aa7d2ac26f8c246d3e815 Mon Sep 17 00:00:00 2001 From: Canmi <9997200@qq.com> Date: Tue, 15 Apr 2025 00:00:14 +0800 Subject: [PATCH 30/30] fix: github ouath fail error --- oauth_auth.py | 129 +++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 111 insertions(+), 18 deletions(-) diff --git a/oauth_auth.py b/oauth_auth.py index d4ff098..4313041 100644 --- a/oauth_auth.py +++ b/oauth_auth.py @@ -676,8 +676,7 @@ class OAuthHandler: auth_btn = self.browser.ele(f"xpath:{selector}", timeout=2) if auth_btn and auth_btn.is_displayed(): break - except Exception as e: - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}") + except: continue if not auth_btn: @@ -704,13 +703,13 @@ class OAuthHandler: try: if self.browser: self.browser.quit() - except Exception as e: - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.quit_browser_error', error=str(e)) if self.translator else f'Error quitting browser: {str(e)}'}{Style.RESET_ALL}") - + 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 @@ -747,8 +746,7 @@ class OAuthHandler: auth_btn = self.browser.ele(f"xpath:{selector}", timeout=1) if auth_btn and auth_btn.is_displayed(): break - except Exception as e: - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.selector_error', error=str(e)) if self.translator else f'Error selecting auth button: {str(e)}'}{Style.RESET_ALL}") + except: continue if auth_btn: break @@ -810,20 +808,115 @@ class OAuthHandler: 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.found_usage_count', usage=usage_text) if self.translator else f'Found usage count: {usage_text}'}{Style.RESET_ALL}") + 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_find_usage_count', error=str(e)) if self.translator else f'Could not find usage count: {str(e)}'}{Style.RESET_ALL}") - - return True, actual_email - except Exception as e: - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_checking_cookies', error=str(e)) if self.translator else f'Error checking cookies: {str(e)}'}{Style.RESET_ALL}") - time.sleep(5) + 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} - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.timeout') if self.translator else 'Timeout during authentication'}{Style.RESET_ALL}") + # 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", "") + token = get_token_from_cookie(value, self.translator) + 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 - except Exception as e: - print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.failed', error=str(e)) if self.translator else f'OAuth failed: {str(e)}'}{Style.RESET_ALL}") + + 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"""