diff --git a/.SRCINFO b/.SRCINFO index d143943..a732162 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.10.g5863891 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 @@ -13,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/.env b/.env index 05af5ad..8449e55 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -version=1.9.03 -VERSION=1.9.03 +version=1.9.05 +VERSION=1.9.05 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 diff --git a/CHANGELOG.md b/CHANGELOG.md index f2f0f41..6c9effa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,20 @@ # Change Log -## v1.9.03 +## 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 相同 +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/PKGBUILD b/PKGBUILD index 97c5636..1b81dda 100644 --- a/PKGBUILD +++ b/PKGBUILD @@ -2,17 +2,17 @@ # Contributor: Canmi (Canmi21) pkgname=cursor-free-vip-git -pkgver=1.9.03.2.g43a58db +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" -license=('MIT') +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,5 +28,7 @@ build() { } package() { + install -Dm644 "$srcdir/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/mit_license" + install -Dm644 "$srcdir/cursor-free-vip/LICENSE.md" "$pkgdir/usr/share/licenses/$pkgname/attribution_non_commercial_no_derivatives_license" install -Dm755 "$srcdir/cursor-free-vip/dist/cursor-free-vip" "$pkgdir/usr/bin/cursor-free-vip" } \ No newline at end of file diff --git a/README.md b/README.md index 2991a4e..29c1a5c 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,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) +[![Release](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/release/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/releases/latest) [![License: CC BY-NC-ND 4.0](https://img.shields.io/badge/License-CC_BY--NC--ND_4.0-lightgrey.svg)](https://creativecommons.org/licenses/by-nc-nd/4.0/) -[![Stars](https://img.shields.io/github/stars/yeongpin/cursor-free-vip?style=flat-square&logo=github)](https://github.com/yeongpin/cursor-free-vip/stargazers) -[![Download](https://img.shields.io/github/downloads/yeongpin/cursor-free-vip/total?style=flat-square&logo=github&color=52c41a1)](https://github.com/yeongpin/cursor-free-vip/releases/latest) +[![Stars](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/stars/yeongpin/cursor-free-vip)](https://github.com/yeongpin/cursor-free-vip/stargazers) +[![Downloads](https://img.shields.io/endpoint?url=https://www.pinnumber.rr.nu/badges/downloads/yeongpin/cursor-free-vip/total)](https://github.com/yeongpin/cursor-free-vip/releases/latest) Buy Me a Coffee

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/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/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: diff --git a/oauth_auth.py b/oauth_auth.py index f70e243..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 @@ -176,10 +177,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, google-chrome-stable" + ) + 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}") @@ -239,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': { @@ -305,7 +311,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 +320,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 +329,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 的 @@ -420,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: @@ -428,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() @@ -438,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""" @@ -473,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'): @@ -955,7 +947,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") @@ -968,11 +961,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): @@ -1014,7 +1009,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): 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"]