forked from mirrors/cursor-free-vip
Compare commits
28 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b64e54e90 | ||
|
|
f667da64b3 | ||
|
|
26a8e8da28 | ||
|
|
3862176867 | ||
|
|
5adc598661 | ||
|
|
5e6651bb32 | ||
|
|
bdc606ce2d | ||
|
|
564e421288 | ||
|
|
dce359dc33 | ||
|
|
ff79fae77b | ||
|
|
db3a2032dc | ||
|
|
e2a33d178d | ||
|
|
42d97cfa87 | ||
|
|
c42d7d5422 | ||
|
|
c7a84ca59f | ||
|
|
4746af7ce9 | ||
|
|
9f51ba8128 | ||
|
|
9aa09c436e | ||
|
|
1e3e9c99eb | ||
|
|
3f9cbc3d08 | ||
|
|
12d46d5f18 | ||
|
|
6cb3ad79af | ||
|
|
6470c65f8b | ||
|
|
491b227486 | ||
|
|
96c0cd5274 | ||
|
|
60a438e618 | ||
|
|
6a25871366 | ||
|
|
b46a58bd23 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -6,7 +6,7 @@ on:
|
||||
version:
|
||||
description: 'Version number (e.g. 1.0.9)'
|
||||
required: true
|
||||
default: '1.8.08'
|
||||
default: '1.9.02'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -241,4 +241,4 @@ jobs:
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
34
CHANGELOG.md
34
CHANGELOG.md
@@ -1,5 +1,37 @@
|
||||
# Change Log
|
||||
|
||||
## v1.9.02
|
||||
1. Add: Bypass Cursor JWT EXP Problem | 添加繞過 Cursor JWT EXP 問題
|
||||
2. Fix: Cursor editor redirects to logout page and logout automatically | 修復 Cursor 編輯器重定向到登出頁面並自動登出
|
||||
3. Fix: Config File Path | 修復配置文件路徑
|
||||
4. Fix: window user permission | 修復 window 用戶權限
|
||||
5. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.9.01
|
||||
1. Add: Bypass Token Limit | 添加繞過 Token 限制
|
||||
2. Add: More Browser Support | 添加更多瀏覽器支持
|
||||
3. Support: Add Opera, Brave, Edge, Firefox | 添加支持 Opera, Brave, Edge, Firefox
|
||||
4. Add config manual browser path | 添加配置手動選擇遊覽器路徑
|
||||
5. Fix: Browser Profile Selection | 修復瀏覽器配置文件選擇
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
|
||||
## v1.8.10
|
||||
1. Add: Check User Authorized | 添加檢查用戶授權
|
||||
2. Fix: Linux Reset Process Error: 'base' | 修復 Linux 重置過程錯誤:'base'
|
||||
3. Updated the get_workbench_cursor_path function to handle Linux systems more effectively. | 更新 get_workbench_cursor_path 函數以更有效地處理 Linux 系統
|
||||
4. Added logic to use the first base path if no valid paths are found in the existing loop. | 添加邏輯以在找不到有效路徑時使用第一個基礎路徑
|
||||
5. Improved maintainability and clarity of the code by explicitly handling different operating systems. | 通過明確處理不同的操作系統,顯著提高了代碼的可維護性和清晰性
|
||||
6. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.09
|
||||
1. Add: Bypass Token Limit Check | 繞過 Token 使用限制檢查
|
||||
2. Add:Bypass Claude Limit 30000 set to 900000(9e5) | 繞過 Claude 使用限制 30000 設置為 900000(9e5)
|
||||
3. Add: Force Update Config | 添加強制更新配置
|
||||
4. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
|
||||
5. Fix: Reset break | 修復重置中斷
|
||||
4. Fix: Some Issues | 修復一些問題
|
||||
|
||||
## v1.8.08
|
||||
1. Add: Force Update Config | 添加強制更新配置
|
||||
2. Add: Multilanguage support for force update | 添加強制更新功能的多語言支持
|
||||
@@ -255,7 +287,7 @@ These changes make the application more user-friendly by only requesting admin p
|
||||
1. Fix: Cursor Auth | 修復 Cursor Auth
|
||||
2. Add: Create Account Maximum Retry | 增加創建賬號最大重試次數
|
||||
3. Fix: Cursor Auth Error | 修復 Cursor Auth 錯誤
|
||||
4. Fix: Update Curl Faild | 修復更新 Curl 失敗
|
||||
4. Fix: Update Curl Failed | 修復更新 Curl 失敗
|
||||
|
||||
## v1.5.03
|
||||
1. HOTFIX: Stuck on starting browser | 修復啟動瀏覽器卡住問題
|
||||
|
||||
33
README.md
33
README.md
@@ -30,9 +30,11 @@ Always clean your browser's cache and cookies. If possible, use a VPN to create
|
||||
<img src="./images/pro_2025-04-05_18-47-56.png" alt="new" width="800" style="border-radius: 6px;"/><br>
|
||||
</p>
|
||||
|
||||
##### If you don't have Google Chrome, you can download it from [here](https://www.google.com/intl/en_pk/chrome/)
|
||||
##### If you don't have browser, you can download it from
|
||||
[Google Chrome](https://www.google.com/intl/en_pk/chrome/) or [Opera](https://www.opera.com/download) or [Edge](https://www.microsoft.com/en-us/edge) or [Firefox](https://www.mozilla.org/en-US/firefox/new/) or [Brave](https://www.brave.com/download/)
|
||||
|
||||
##### 如果沒有 Google Chrome,可以從[這裡](https://www.google.com/intl/en_pk/chrome/)下載
|
||||
##### 如果沒有瀏覽器,可以從
|
||||
[Google Chrome](https://www.google.com/intl/en_pk/chrome/) 或 [Opera](https://www.opera.com/download) 或 [Edge](https://www.microsoft.com/en-us/edge) 或 [Firefox](https://www.mozilla.org/en-US/firefox/new/) 或 [Brave](https://www.brave.com/download/) 下載
|
||||
|
||||
</div>
|
||||
|
||||
@@ -166,6 +168,33 @@ max_timeout = 160
|
||||
check_update = True
|
||||
# Show Account Info | 顯示賬號信息
|
||||
show_account_info = True
|
||||
|
||||
[WindowsPaths]
|
||||
storage_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\storage.json
|
||||
sqlite_path = C:\Users\yeongpin\AppData\Roaming\Cursor\User\globalStorage\state.vscdb
|
||||
machine_id_path = C:\Users\yeongpin\AppData\Roaming\Cursor\machineId
|
||||
cursor_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app
|
||||
updater_path = C:\Users\yeongpin\AppData\Local\cursor-updater
|
||||
update_yml_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app-update.yml
|
||||
product_json_path = C:\Users\yeongpin\AppData\Local\Programs\Cursor\resources\app\product.json
|
||||
|
||||
[Browser]
|
||||
default_browser = opera
|
||||
chrome_path = C:\Program Files\Google\Chrome\Application\chrome.exe
|
||||
edge_path = C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe
|
||||
firefox_path = C:\Program Files\Mozilla Firefox\firefox.exe
|
||||
brave_path = C:\Program Files\BraveSoftware/Brave-Browser/Application/brave.exe
|
||||
chrome_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
edge_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\msedgedriver.exe
|
||||
firefox_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\geckodriver.exe
|
||||
brave_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
opera_path = C:\Users\yeongpin\AppData\Local\Programs\Opera\opera.exe
|
||||
opera_driver_path = D:\VisualCode\cursor-free-vip-new\drivers\chromedriver.exe
|
||||
|
||||
[OAuth]
|
||||
show_selection_alert = False
|
||||
timeout = 120
|
||||
max_attempts = 3
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
174
bypass_token_limit.py
Normal file
174
bypass_token_limit.py
Normal file
@@ -0,0 +1,174 @@
|
||||
import os
|
||||
import shutil
|
||||
import platform
|
||||
import tempfile
|
||||
import glob
|
||||
from colorama import Fore, Style, init
|
||||
import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
from config import get_config
|
||||
from datetime import datetime
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"FILE": "📄",
|
||||
"BACKUP": "💾",
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"RESET": "🔄",
|
||||
"WARNING": "⚠️",
|
||||
}
|
||||
|
||||
def get_workbench_cursor_path(translator=None) -> str:
|
||||
"""Get Cursor workbench.desktop.main.js path"""
|
||||
system = platform.system()
|
||||
|
||||
# Read configuration
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
if os.path.exists(config_file):
|
||||
config.read(config_file)
|
||||
|
||||
paths_map = {
|
||||
"Darwin": { # macOS
|
||||
"base": "/Applications/Cursor.app/Contents/Resources/app",
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
},
|
||||
"Windows": {
|
||||
"main": "out\\vs\\workbench\\workbench.desktop.main.js"
|
||||
},
|
||||
"Linux": {
|
||||
"bases": ["/opt/Cursor/resources/app", "/usr/share/cursor/resources/app", "/usr/lib/cursor/app/"],
|
||||
"main": "out/vs/workbench/workbench.desktop.main.js"
|
||||
}
|
||||
}
|
||||
|
||||
if system == "Linux":
|
||||
# Add extracted AppImage with correct usr structure
|
||||
extracted_usr_paths = glob.glob(os.path.expanduser("~/squashfs-root/usr/share/cursor/resources/app"))
|
||||
|
||||
paths_map["Linux"]["bases"].extend(extracted_usr_paths)
|
||||
|
||||
if system not in paths_map:
|
||||
raise OSError(translator.get('reset.unsupported_os', system=system) if translator else f"不支持的操作系统: {system}")
|
||||
|
||||
if system == "Linux":
|
||||
for base in paths_map["Linux"]["bases"]:
|
||||
main_path = os.path.join(base, paths_map["Linux"]["main"])
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} Checking path: {main_path}{Style.RESET_ALL}")
|
||||
if os.path.exists(main_path):
|
||||
return main_path
|
||||
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
elif system == "Darwin":
|
||||
base_path = paths_map[system]["base"]
|
||||
else: # Linux
|
||||
# For Linux, we've already checked all bases in the loop above
|
||||
# If we're here, it means none of the bases worked, so we'll use the first one
|
||||
base_path = paths_map[system]["bases"][0]
|
||||
|
||||
main_path = os.path.join(base_path, paths_map[system]["main"])
|
||||
|
||||
if not os.path.exists(main_path):
|
||||
raise OSError(translator.get('reset.file_not_found', path=main_path) if translator else f"未找到 Cursor main.js 文件: {main_path}")
|
||||
|
||||
return main_path
|
||||
|
||||
|
||||
def modify_workbench_js(file_path: str, translator=None) -> bool:
|
||||
"""
|
||||
Modify file content
|
||||
"""
|
||||
try:
|
||||
# Save original file permissions
|
||||
original_stat = os.stat(file_path)
|
||||
original_mode = original_stat.st_mode
|
||||
original_uid = original_stat.st_uid
|
||||
original_gid = original_stat.st_gid
|
||||
|
||||
# Create temporary file
|
||||
with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", errors="ignore", delete=False) as tmp_file:
|
||||
# Read original content
|
||||
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
|
||||
content = main_file.read()
|
||||
|
||||
patterns = {
|
||||
# 通用按钮替换模式
|
||||
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Windows/Linux/Mac 通用按钮替换模式
|
||||
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Badge 替换
|
||||
r'<div>Pro Trial': r'<div>Pro',
|
||||
|
||||
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
|
||||
|
||||
#
|
||||
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
|
||||
# Pro
|
||||
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
|
||||
|
||||
# Toast 替换
|
||||
r'notifications-toasts': r'notifications-toasts hidden'
|
||||
}
|
||||
|
||||
# 使用patterns进行替换
|
||||
for old_pattern, new_pattern in patterns.items():
|
||||
content = content.replace(old_pattern, new_pattern)
|
||||
|
||||
# Write to temporary file
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
# Backup original file with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{file_path}.backup.{timestamp}"
|
||||
shutil.copy2(file_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
|
||||
# Move temporary file to original position
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
shutil.move(tmp_path, file_path)
|
||||
|
||||
# Restore original permissions
|
||||
os.chmod(file_path, original_mode)
|
||||
if os.name != "nt": # Not Windows
|
||||
os.chown(file_path, original_uid, original_gid)
|
||||
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.file_modified')}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('reset.modify_file_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
if "tmp_path" in locals():
|
||||
try:
|
||||
os.unlink(tmp_path)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
config = get_config(translator)
|
||||
if not config:
|
||||
return False
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('bypass_token_limit.title')}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
|
||||
modify_workbench_js(get_workbench_cursor_path(translator), translator)
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
|
||||
input(f"{EMOJI['INFO']} {translator.get('bypass_token_limit.press_enter')}...")
|
||||
|
||||
if __name__ == "__main__":
|
||||
from main import translator as main_translator
|
||||
run(main_translator)
|
||||
214
check_user_authorized.py
Normal file
214
check_user_authorized.py
Normal file
@@ -0,0 +1,214 @@
|
||||
import os
|
||||
import requests
|
||||
import time
|
||||
import hashlib
|
||||
import base64
|
||||
import struct
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
"SUCCESS": "✅",
|
||||
"ERROR": "❌",
|
||||
"INFO": "ℹ️",
|
||||
"WARNING": "⚠️",
|
||||
"KEY": "🔑",
|
||||
"CHECK": "🔍"
|
||||
}
|
||||
|
||||
def generate_hashed64_hex(input_str: str, salt: str = '') -> str:
|
||||
"""Generate a SHA-256 hash of input + salt and return as hex"""
|
||||
hash_obj = hashlib.sha256()
|
||||
hash_obj.update((input_str + salt).encode('utf-8'))
|
||||
return hash_obj.hexdigest()
|
||||
|
||||
def obfuscate_bytes(byte_array: bytearray) -> bytearray:
|
||||
"""Obfuscate bytes using the algorithm from utils.js"""
|
||||
t = 165
|
||||
for r in range(len(byte_array)):
|
||||
byte_array[r] = ((byte_array[r] ^ t) + (r % 256)) & 0xFF
|
||||
t = byte_array[r]
|
||||
return byte_array
|
||||
|
||||
def generate_cursor_checksum(token: str, translator=None) -> str:
|
||||
"""Generate Cursor checksum from token using the algorithm"""
|
||||
try:
|
||||
# Clean the token
|
||||
clean_token = token.strip()
|
||||
|
||||
# Generate machineId and macMachineId
|
||||
machine_id = generate_hashed64_hex(clean_token, 'machineId')
|
||||
mac_machine_id = generate_hashed64_hex(clean_token, 'macMachineId')
|
||||
|
||||
# Get timestamp and convert to byte array
|
||||
timestamp = int(time.time() * 1000) // 1000000
|
||||
byte_array = bytearray(struct.pack('>Q', timestamp)[-6:]) # Take last 6 bytes
|
||||
|
||||
# Obfuscate bytes and encode as base64
|
||||
obfuscated_bytes = obfuscate_bytes(byte_array)
|
||||
encoded_checksum = base64.b64encode(obfuscated_bytes).decode('utf-8')
|
||||
|
||||
# Combine final checksum
|
||||
return f"{encoded_checksum}{machine_id}/{mac_machine_id}"
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.error_generating_checksum', error=str(e)) if translator else f'Error generating checksum: {str(e)}'}{Style.RESET_ALL}")
|
||||
return ""
|
||||
|
||||
def check_user_authorized(token: str, translator=None) -> bool:
|
||||
"""
|
||||
Check if the user is authorized with the given token
|
||||
|
||||
Args:
|
||||
token (str): The authorization token
|
||||
translator: Optional translator for internationalization
|
||||
|
||||
Returns:
|
||||
bool: True if authorized, False otherwise
|
||||
"""
|
||||
try:
|
||||
print(f"{Fore.CYAN}{EMOJI['CHECK']} {translator.get('auth_check.checking_authorization') if translator else 'Checking authorization...'}{Style.RESET_ALL}")
|
||||
|
||||
# Clean the token
|
||||
if token and '%3A%3A' in token:
|
||||
token = token.split('%3A%3A')[1]
|
||||
elif token and '::' in token:
|
||||
token = token.split('::')[1]
|
||||
|
||||
# Remove any whitespace
|
||||
token = token.strip()
|
||||
|
||||
if not token or len(token) < 10: # Add a basic validation for token length
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.invalid_token') if translator else 'Invalid token'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_length', length=len(token)) if translator else f'Token length: {len(token)} characters'}{Style.RESET_ALL}")
|
||||
|
||||
# Try to get usage info using the DashboardService API
|
||||
try:
|
||||
# Generate checksum
|
||||
checksum = generate_cursor_checksum(token, translator)
|
||||
|
||||
# Create request headers
|
||||
headers = {
|
||||
'accept-encoding': 'gzip',
|
||||
'authorization': f'Bearer {token}',
|
||||
'connect-protocol-version': '1',
|
||||
'content-type': 'application/proto',
|
||||
'user-agent': 'connect-es/1.6.1',
|
||||
'x-cursor-checksum': checksum,
|
||||
'x-cursor-client-version': '0.48.7',
|
||||
'x-cursor-timezone': 'Asia/Shanghai',
|
||||
'x-ghost-mode': 'false',
|
||||
'Host': 'api2.cursor.sh'
|
||||
}
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.checking_usage_information') if translator else 'Checking usage information...'}{Style.RESET_ALL}")
|
||||
|
||||
# Make the request - this endpoint doesn't need a request body
|
||||
usage_response = requests.post(
|
||||
'https://api2.cursor.sh/aiserver.v1.DashboardService/GetUsageBasedPremiumRequests',
|
||||
headers=headers,
|
||||
data=b'', # Empty body
|
||||
timeout=10
|
||||
)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.usage_response', response=usage_response.status_code) if translator else f'Usage response status: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
if usage_response.status_code == 200:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.user_authorized') if translator else 'User is authorized'}{Style.RESET_ALL}")
|
||||
return True
|
||||
elif usage_response.status_code == 401 or usage_response.status_code == 403:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.user_unauthorized') if translator else 'User is unauthorized'}{Style.RESET_ALL}")
|
||||
return False
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.unexpected_status_code', code=usage_response.status_code) if translator else f'Unexpected status code: {usage_response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
# If the token at least looks like a valid JWT, consider it valid
|
||||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} Error checking usage: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# If the token at least looks like a valid JWT, consider it valid even if the API check fails
|
||||
if token.startswith('eyJ') and '.' in token and len(token) > 100:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.jwt_token_warning') if translator else 'Token appears to be in JWT format, but API check failed. The token might be valid but API access is restricted.'}{Style.RESET_ALL}")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.request_timeout') if translator else 'Request timed out'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.connection_error') if translator else 'Connection error'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.check_error', error=str(e)) if translator else f'Error checking authorization: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def run(translator=None):
|
||||
"""Run function to be called from main.py"""
|
||||
try:
|
||||
# Ask user if they want to get token from database or input manually
|
||||
choice = input(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.token_source') if translator else 'Get token from database or input manually? (d/m, default: d): '}{Style.RESET_ALL}").strip().lower()
|
||||
|
||||
token = None
|
||||
|
||||
# If user chooses database or default
|
||||
if not choice or choice == 'd':
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('auth_check.getting_token_from_db') if translator else 'Getting token from database...'}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Import functions from cursor_acc_info.py
|
||||
from cursor_acc_info import get_token
|
||||
|
||||
# Get token using the get_token function
|
||||
token = get_token()
|
||||
|
||||
if token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.token_found_in_db') if translator else 'Token found in database'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.token_not_found_in_db') if translator else 'Token not found in database'}{Style.RESET_ALL}")
|
||||
except ImportError:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.cursor_acc_info_not_found') if translator else 'cursor_acc_info.py not found'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.error_getting_token_from_db', error=str(e)) if translator else f'Error getting token from database: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# If token not found in database or user chooses manual input
|
||||
if not token:
|
||||
# Try to get token from environment
|
||||
token = os.environ.get('CURSOR_TOKEN')
|
||||
|
||||
# If not in environment, ask user to input
|
||||
if not token:
|
||||
token = input(f"{Fore.CYAN}{EMOJI['KEY']} {translator.get('auth_check.enter_token') if translator else 'Enter your Cursor token: '}{Style.RESET_ALL}")
|
||||
|
||||
# Check authorization
|
||||
is_authorized = check_user_authorized(token, translator)
|
||||
|
||||
if is_authorized:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('auth_check.authorization_successful') if translator else 'Authorization successful!'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.authorization_failed') if translator else 'Authorization failed!'}{Style.RESET_ALL}")
|
||||
|
||||
return is_authorized
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('auth_check.operation_cancelled') if translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('auth_check.unexpected_error', error=str(e)) if translator else f'Unexpected error: {str(e)}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def main(translator=None):
|
||||
"""Main function to check user authorization"""
|
||||
return run(translator)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
115
config.py
115
config.py
@@ -2,7 +2,7 @@ import os
|
||||
import sys
|
||||
import configparser
|
||||
from colorama import Fore, Style
|
||||
from utils import get_user_documents_path, get_default_chrome_path, get_linux_cursor_path
|
||||
from utils import get_user_documents_path, get_linux_cursor_path, get_default_driver_path, get_default_browser_path
|
||||
import shutil
|
||||
import datetime
|
||||
|
||||
@@ -18,19 +18,60 @@ EMOJI = {
|
||||
"SETTINGS": "⚙️"
|
||||
}
|
||||
|
||||
# global config cache
|
||||
_config_cache = None
|
||||
|
||||
def setup_config(translator=None):
|
||||
"""Setup configuration file and return config object"""
|
||||
try:
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
# get documents path
|
||||
docs_path = get_user_documents_path()
|
||||
if not docs_path or not os.path.exists(docs_path):
|
||||
# if documents path not found, use current directory
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.documents_path_not_found', fallback='Documents path not found, using current directory') if translator else 'Documents path not found, using current directory'}{Style.RESET_ALL}")
|
||||
docs_path = os.path.abspath('.')
|
||||
|
||||
# normalize path
|
||||
config_dir = os.path.normpath(os.path.join(docs_path, ".cursor-free-vip"))
|
||||
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
|
||||
|
||||
# create config directory, only print message when directory not exists
|
||||
dir_exists = os.path.exists(config_dir)
|
||||
try:
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
if not dir_exists: # only print message when directory not exists
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_dir_created', path=config_dir) if translator else f'Config directory created: {config_dir}'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
# if cannot create directory, use temporary directory
|
||||
import tempfile
|
||||
temp_dir = os.path.normpath(os.path.join(tempfile.gettempdir(), ".cursor-free-vip"))
|
||||
temp_exists = os.path.exists(temp_dir)
|
||||
config_dir = temp_dir
|
||||
config_file = os.path.normpath(os.path.join(config_dir, "config.ini"))
|
||||
os.makedirs(config_dir, exist_ok=True)
|
||||
if not temp_exists: # only print message when temporary directory not exists
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('config.using_temp_dir', path=config_dir, error=str(e)) if translator else f'Using temporary directory due to error: {config_dir} (Error: {str(e)})'}{Style.RESET_ALL}")
|
||||
|
||||
# create config object
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# Default configuration
|
||||
default_config = {
|
||||
'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_driver_path': get_default_driver_path('edge'),
|
||||
'firefox_driver_path': get_default_driver_path('firefox'),
|
||||
'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_chrome_path()
|
||||
'chromepath': get_default_browser_path('chrome')
|
||||
},
|
||||
'Turnstile': {
|
||||
'handle_turnstile_time': '2',
|
||||
@@ -54,7 +95,17 @@ def setup_config(translator=None):
|
||||
},
|
||||
'Utils': {
|
||||
'enabled_update_check': 'True',
|
||||
'enabled_force_update': 'False',
|
||||
'enabled_account_info': 'True'
|
||||
},
|
||||
'OAuth': {
|
||||
'show_selection_alert': False, # 默认不显示选择提示弹窗
|
||||
'timeout': 120,
|
||||
'max_attempts': 3
|
||||
},
|
||||
'Token': {
|
||||
'refresh_server': 'https://token.cursorpro.com.cn',
|
||||
'enable_refresh': True
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +312,7 @@ def print_config(config, translator=None):
|
||||
|
||||
def force_update_config(translator=None):
|
||||
"""
|
||||
Force update configuration file with latest defaults
|
||||
Force update configuration file with latest defaults if update check is enabled.
|
||||
Args:
|
||||
translator: Translator instance
|
||||
Returns:
|
||||
@@ -271,26 +322,39 @@ def force_update_config(translator=None):
|
||||
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
|
||||
config_file = os.path.join(config_dir, "config.ini")
|
||||
current_time = datetime.datetime.now()
|
||||
|
||||
|
||||
# If the config file exists, check if forced update is enabled
|
||||
if os.path.exists(config_file):
|
||||
try:
|
||||
# create backup
|
||||
backup_file = f"{config_file}.bak.{current_time.strftime('%Y%m%d_%H%M%S')}"
|
||||
shutil.copy2(config_file, backup_file)
|
||||
# First, read the existing configuration
|
||||
existing_config = configparser.ConfigParser()
|
||||
existing_config.read(config_file, encoding='utf-8')
|
||||
# Check if "enabled_update_check" is True
|
||||
update_enabled = True # Default to True if not set
|
||||
if existing_config.has_section('Utils') and existing_config.has_option('Utils', 'enabled_force_update'):
|
||||
update_enabled = existing_config.get('Utils', 'enabled_force_update').strip().lower() in ('true', 'yes', '1', 'on')
|
||||
|
||||
if update_enabled:
|
||||
try:
|
||||
# Create a backup
|
||||
backup_file = f"{config_file}.bak.{current_time.strftime('%Y%m%d_%H%M%S')}"
|
||||
shutil.copy2(config_file, backup_file)
|
||||
if translator:
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.backup_created', path=backup_file) if translator else f'Backup created: {backup_file}'}{Style.RESET_ALL}")
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_enabled') if translator else 'Config file force update enabled'}{Style.RESET_ALL}")
|
||||
# Delete the original config file (forced update)
|
||||
os.remove(config_file)
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_removed') if translator else 'Config file removed for forced update'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.backup_failed', error=str(e)) if translator else f'Failed to backup config: {str(e)}'}{Style.RESET_ALL}")
|
||||
else:
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.backup_created', path=backup_file) if translator else f'Backup created: {backup_file}'}{Style.RESET_ALL}")
|
||||
|
||||
# delete original file
|
||||
os.remove(config_file)
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_removed') if translator else 'Config file removed for forced update'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.backup_failed', error=str(e)) if translator else f'Failed to backup config: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# use existing setup_config function to create new config
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {translator.get('config.config_force_update_disabled', fallback='Config file force update disabled by configuration. Keeping existing config file.') if translator else 'Config file force update disabled by configuration. Keeping existing config file.'}{Style.RESET_ALL}")
|
||||
|
||||
# Generate a new (or updated) configuration if needed
|
||||
return setup_config(translator)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
if translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('config.force_update_failed', error=str(e)) if translator else f'Force update config failed: {str(e)}'}{Style.RESET_ALL}")
|
||||
@@ -298,4 +362,7 @@ def force_update_config(translator=None):
|
||||
|
||||
def get_config(translator=None):
|
||||
"""Get existing config or create new one"""
|
||||
return setup_config(translator)
|
||||
global _config_cache
|
||||
if _config_cache is None:
|
||||
_config_cache = setup_config(translator)
|
||||
return _config_cache
|
||||
112
get_user_token.py
Normal file
112
get_user_token.py
Normal file
@@ -0,0 +1,112 @@
|
||||
import requests
|
||||
import json
|
||||
import time
|
||||
from colorama import Fore, Style
|
||||
import os
|
||||
from config import get_config
|
||||
|
||||
# Define emoji constants
|
||||
EMOJI = {
|
||||
'START': '🚀',
|
||||
'OAUTH': '🔑',
|
||||
'SUCCESS': '✅',
|
||||
'ERROR': '❌',
|
||||
'WAIT': '⏳',
|
||||
'INFO': 'ℹ️',
|
||||
'WARNING': '⚠️'
|
||||
}
|
||||
|
||||
def refresh_token(token, translator=None):
|
||||
"""Refresh the token using the Chinese server API
|
||||
|
||||
Args:
|
||||
token (str): The full WorkosCursorSessionToken cookie value
|
||||
translator: Optional translator object
|
||||
|
||||
Returns:
|
||||
str: The refreshed access token or original token if refresh fails
|
||||
"""
|
||||
try:
|
||||
config = get_config(translator)
|
||||
# Get refresh_server URL from config or use default
|
||||
refresh_server = config.get('Token', 'refresh_server', fallback='https://token.cursorpro.com.cn')
|
||||
|
||||
# Ensure the token is URL encoded properly
|
||||
if '%3A%3A' not in token and '::' in token:
|
||||
# Replace :: with URL encoded version if needed
|
||||
token = token.replace('::', '%3A%3A')
|
||||
|
||||
# Make the request to the refresh server
|
||||
url = f"{refresh_server}/reftoken?token={token}"
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('token.refreshing') if translator else 'Refreshing token...'}{Style.RESET_ALL}")
|
||||
|
||||
response = requests.get(url, timeout=30)
|
||||
|
||||
if response.status_code == 200:
|
||||
try:
|
||||
data = response.json()
|
||||
|
||||
if data.get('code') == 0 and data.get('msg') == "获取成功":
|
||||
access_token = data.get('data', {}).get('accessToken')
|
||||
days_left = data.get('data', {}).get('days_left', 0)
|
||||
expire_time = data.get('data', {}).get('expire_time', 'Unknown')
|
||||
|
||||
if access_token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('token.refresh_success', days=days_left, expire=expire_time) if translator else f'Token refreshed successfully! Valid for {days_left} days (expires: {expire_time})'}{Style.RESET_ALL}")
|
||||
return access_token
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['WARNING']} {translator.get('token.no_access_token') if translator else 'No access token in response'}{Style.RESET_ALL}")
|
||||
else:
|
||||
error_msg = data.get('msg', 'Unknown error')
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.refresh_failed', error=error_msg) if translator else f'Token refresh failed: {error_msg}'}{Style.RESET_ALL}")
|
||||
except json.JSONDecodeError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.invalid_response') if translator else 'Invalid JSON response from refresh server'}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.server_error', status=response.status_code) if translator else f'Refresh server error: HTTP {response.status_code}'}{Style.RESET_ALL}")
|
||||
|
||||
except requests.exceptions.Timeout:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.request_timeout') if translator else 'Request to refresh server timed out'}{Style.RESET_ALL}")
|
||||
except requests.exceptions.ConnectionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.connection_error') if translator else 'Connection error to refresh server'}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.unexpected_error', error=str(e)) if translator else f'Unexpected error during token refresh: {str(e)}'}{Style.RESET_ALL}")
|
||||
|
||||
# Return original token if refresh fails
|
||||
return token.split('%3A%3A')[-1] if '%3A%3A' in token else token.split('::')[-1] if '::' in token else token
|
||||
|
||||
def get_token_from_cookie(cookie_value, translator=None):
|
||||
"""Extract and process token from cookie value
|
||||
|
||||
Args:
|
||||
cookie_value (str): The WorkosCursorSessionToken cookie value
|
||||
translator: Optional translator object
|
||||
|
||||
Returns:
|
||||
str: The processed token
|
||||
"""
|
||||
try:
|
||||
# Try to refresh the token with the API first
|
||||
refreshed_token = refresh_token(cookie_value, translator)
|
||||
|
||||
# If refresh succeeded and returned a different token, use it
|
||||
if refreshed_token and refreshed_token != cookie_value:
|
||||
return refreshed_token
|
||||
|
||||
# If refresh failed or returned same token, use traditional extraction method
|
||||
if '%3A%3A' in cookie_value:
|
||||
return cookie_value.split('%3A%3A')[-1]
|
||||
elif '::' in cookie_value:
|
||||
return cookie_value.split('::')[-1]
|
||||
else:
|
||||
return cookie_value
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('token.extraction_error', error=str(e)) if translator else f'Error extracting token: {str(e)}'}{Style.RESET_ALL}")
|
||||
# Fall back to original behavior
|
||||
if '%3A%3A' in cookie_value:
|
||||
return cookie_value.split('%3A%3A')[-1]
|
||||
elif '::' in cookie_value:
|
||||
return cookie_value.split('::')[-1]
|
||||
else:
|
||||
return cookie_value
|
||||
@@ -30,7 +30,9 @@
|
||||
"continue_prompt": "Continue? (y/N): ",
|
||||
"operation_cancelled_by_user": "Operation cancelled by user",
|
||||
"exiting": "Exiting ……",
|
||||
"bypass_version_check": "Bypass Cursor Version Check"
|
||||
"bypass_version_check": "Bypass Cursor Version Check",
|
||||
"check_user_authorized": "Check User Authorized",
|
||||
"bypass_token_limit": "Bypass Token Limit"
|
||||
},
|
||||
"languages": {
|
||||
"en": "English",
|
||||
@@ -197,7 +199,15 @@
|
||||
"setting_on_password": "Setting Password",
|
||||
"getting_code": "Getting Verification Code, Will Try in 60s",
|
||||
"human_verify_error": "Can't verify the user is human. Retrying...",
|
||||
"max_retries_reached": "Maximum retry attempts reached. Registration failed."
|
||||
"max_retries_reached": "Maximum retry attempts reached. Registration failed.",
|
||||
"browser_path_invalid": "{browser} path is invalid, using default path",
|
||||
"using_browser": "Using {browser} browser: {path}",
|
||||
"using_browser_profile": "Using {browser} profile from: {user_data_dir}",
|
||||
"make_sure_browser_is_properly_installed": "Make sure {browser} is properly installed",
|
||||
"try_install_browser": "Try installing the browser with your package manager",
|
||||
"tracking_processes": "Tracking {count} {browser} processes",
|
||||
"no_new_processes_detected": "No new {browser} processes detected to track",
|
||||
"could_not_track_processes": "Could not track {browser} processes: {error}"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor Auth Manager",
|
||||
@@ -560,7 +570,12 @@
|
||||
"backup_created": "Backup created: {path}",
|
||||
"config_removed": "Config file removed for forced update",
|
||||
"backup_failed": "Failed to backup config: {error}",
|
||||
"force_update_failed": "Force update config failed: {error}"
|
||||
"force_update_failed": "Force update config failed: {error}",
|
||||
"config_force_update_disabled": "Config file force update disabled , skipping forced update",
|
||||
"config_force_update_enabled": "Config file force update enabled , performing forced update",
|
||||
"documents_path_not_found": "Documents path not found, using current directory",
|
||||
"config_dir_created": "Config directory created: {path}",
|
||||
"using_temp_dir": "Using temporary directory due to error: {path} (Error: {error})"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "Authentication button not found",
|
||||
@@ -619,19 +634,27 @@
|
||||
"warning_could_not_kill_existing_browser_processes": "Warning: Could not kill existing browser processes: {error}",
|
||||
"browser_failed_to_start": "Browser failed to start: {error}",
|
||||
"browser_failed": "Browser failed to start: {error}",
|
||||
"browser_failed_to_start_fallback": "Browser failed to start: {error}"
|
||||
"browser_failed_to_start_fallback": "Browser failed to start: {error}",
|
||||
"user_data_dir_not_found": "{browser} user data directory not found at {path}, will try Chrome instead",
|
||||
"error_getting_user_data_directory": "Error getting user data directory: {error}",
|
||||
"warning_browser_close": "Warning: This will close all running {browser} processes",
|
||||
"killing_browser_processes": "Killing {browser} processes...",
|
||||
"profile_selection_error": "Error during profile selection: {error}",
|
||||
"using_configured_browser_path": "Using configured {browser} path: {path}",
|
||||
"browser_not_found_trying_chrome": "Could not find {browser}, trying Chrome instead",
|
||||
"found_chrome_at": "Found Chrome at: {path}",
|
||||
"found_browser_user_data_dir": "Found {browser} user data directory: {path}"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome Profile Selection",
|
||||
"select_profile": "Select a Chrome profile to use:",
|
||||
"profile_list": "Available profiles:",
|
||||
"default_profile": "Default Profile",
|
||||
"browser_profile": {
|
||||
"title": "Browser Profile Selection",
|
||||
"select_profile": "Select {browser} profile to use:",
|
||||
"profile_list": "Available {browser} profiles:",
|
||||
"default_profile": "Default profile",
|
||||
"profile": "Profile {number}",
|
||||
"no_profiles": "No Chrome profiles found",
|
||||
"error_loading": "Error loading Chrome profiles: {error}",
|
||||
"no_profiles": "No {browser} profiles found",
|
||||
"error_loading": "Error loading {browser} profiles: {error}",
|
||||
"profile_selected": "Selected profile: {profile}",
|
||||
"invalid_selection": "Invalid selection. Please try again",
|
||||
"warning_chrome_close": "Warning: This will close all running Chrome processes"
|
||||
"invalid_selection": "Invalid selection. Please try again."
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google Account Deletion Tool",
|
||||
@@ -696,5 +719,50 @@
|
||||
"title": "Cursor Version Bypass Tool",
|
||||
"description": "This tool modifies Cursor's product.json to bypass version restrictions",
|
||||
"menu_option": "Bypass Cursor Version Check"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "Checking authorization...",
|
||||
"token_source": "Get token from database or input manually? (d/m, default: d)",
|
||||
"getting_token_from_db": "Getting token from database...",
|
||||
"token_found_in_db": "Token found in database",
|
||||
"token_not_found_in_db": "Token not found in database",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py not found",
|
||||
"error_getting_token_from_db": "Error getting token from database: {error}",
|
||||
"enter_token": "Enter your Cursor token: ",
|
||||
"token_length": "Token length: {length} characters",
|
||||
"usage_response_status": "Usage response status: {response}",
|
||||
"unexpected_status_code": "Unexpected status code: {code}",
|
||||
"jwt_token_warning": "Token appears to be in JWT format, but API check returned an unexpected status code. The token might be valid but API access is restricted.",
|
||||
"invalid_token": "Invalid token",
|
||||
"user_authorized": "User is authorized",
|
||||
"user_unauthorized": "User is unauthorized",
|
||||
"request_timeout": "Request timed out",
|
||||
"connection_error": "Connection error",
|
||||
"check_error": "Error checking authorization: {error}",
|
||||
"authorization_successful": "Authorization successful!",
|
||||
"authorization_failed": "Authorization failed!",
|
||||
"operation_cancelled": "Operation cancelled by user",
|
||||
"unexpected_error": "Unexpected error: {error}",
|
||||
"error_generating_checksum": "Error generating checksum: {error}",
|
||||
"checking_usage_information": "Checking usage information...",
|
||||
"check_usage_response": "Check usage response: {response}",
|
||||
"usage_response": "Usage response: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "Bypass Token Limit Tool",
|
||||
"description": "This tool modifies the workbench.desktop.main.js file to bypass the token limit",
|
||||
"press_enter": "Press Enter to continue..."
|
||||
},
|
||||
"token": {
|
||||
"refreshing": "Refreshing token...",
|
||||
"refresh_success": "Token refreshed successfully! Valid for {days} days (expires: {expire})",
|
||||
"no_access_token": "No access token in response",
|
||||
"refresh_failed": "Token refresh failed: {error}",
|
||||
"invalid_response": "Invalid JSON response from refresh server",
|
||||
"server_error": "Refresh server error: HTTP {status}",
|
||||
"request_timeout": "Request to refresh server timed out",
|
||||
"connection_error": "Connection error to refresh server",
|
||||
"unexpected_error": "Unexpected error during token refresh: {error}",
|
||||
"extraction_error": "Error extracting token: {error}"
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,9 @@
|
||||
"continue_prompt": "继续?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被用户取消",
|
||||
"exiting": "退出中 ……",
|
||||
"bypass_version_check": "绕过 Cursor 版本检查"
|
||||
"bypass_version_check": "绕过 Cursor 版本检查",
|
||||
"check_user_authorized": "检查用户授权",
|
||||
"bypass_token_limit": "绕过 Token 限制"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英语",
|
||||
@@ -195,7 +197,15 @@
|
||||
"password_submitted": "密码已提交",
|
||||
"total_usage": "总使用量: {usage}",
|
||||
"setting_on_password": "设置密码",
|
||||
"getting_code": "获取验证码,将在60秒内尝试..."
|
||||
"getting_code": "获取验证码,将在60秒内尝试...",
|
||||
"browser_path_invalid": "{browser} 路径无效,使用默认路径",
|
||||
"using_browser": "正在使用 {browser} 浏览器: {path}",
|
||||
"using_browser_profile": "使用 {browser} 配置文件: {user_data_dir}",
|
||||
"make_sure_browser_is_properly_installed": "确保 {browser} 已正确安装",
|
||||
"try_install_browser": "尝试使用包管理器安装浏览器",
|
||||
"tracking_processes": "正在跟踪 {count} 个 {browser} 进程",
|
||||
"no_new_processes_detected": "未检测到新的 {browser} 进程",
|
||||
"could_not_track_processes": "无法跟踪 {browser} 进程: {error}"
|
||||
},
|
||||
"auth": {
|
||||
"title": "Cursor 认证管理器",
|
||||
@@ -538,7 +548,12 @@
|
||||
"backup_created": "备份创建: {path}",
|
||||
"config_removed": "配置文件已删除用于强制更新",
|
||||
"backup_failed": "备份失败: {error}",
|
||||
"force_update_failed": "强制更新配置失败: {error}"
|
||||
"force_update_failed": "强制更新配置失败: {error}",
|
||||
"config_force_update_disabled": "配置文件强制更新已禁用,跳过强制更新",
|
||||
"config_force_update_enabled": "配置文件强制更新已启用,正在执行强制更新",
|
||||
"documents_path_not_found": "找不到文档路径,使用当前目录",
|
||||
"config_dir_created": "已创建配置目录: {path}",
|
||||
"using_temp_dir": "由于错误使用临时目录: {path} (错误: {error})"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到认证按钮",
|
||||
@@ -597,19 +612,27 @@
|
||||
"warning_could_not_kill_existing_browser_processes": "警告: 无法杀死现有浏览器进程: {error}",
|
||||
"browser_failed_to_start": "浏览器启动失败: {error}",
|
||||
"browser_failed": "浏览器启动失败: {error}",
|
||||
"browser_failed_to_start_fallback": "浏览器启动失败: {error}"
|
||||
"browser_failed_to_start_fallback": "浏览器启动失败: {error}",
|
||||
"user_data_dir_not_found": "{browser} 用户数据目录未找到:{path},将尝试使用 Chrome",
|
||||
"error_getting_user_data_directory": "获取用户数据目录出错:{error}",
|
||||
"warning_browser_close": "警告:这将关闭所有正在运行的 {browser} 进程",
|
||||
"killing_browser_processes": "正在关闭 {browser} 进程...",
|
||||
"profile_selection_error": "配置文件选择过程中出错: {error}",
|
||||
"using_configured_browser_path": "使用配置的 {browser} 路径: {path}",
|
||||
"browser_not_found_trying_chrome": "未找到 {browser},尝试使用 Chrome 代替",
|
||||
"found_chrome_at": "找到 Chrome: {path}",
|
||||
"found_browser_user_data_dir": "找到 {browser} 用户数据目录: {path}"
|
||||
},
|
||||
"chrome_profile": {
|
||||
"title": "Chrome配置文件选择",
|
||||
"select_profile": "选择要使用的Chrome配置文件:",
|
||||
"profile_list": "可用配置文件:",
|
||||
"browser_profile": {
|
||||
"title": "浏览器配置文件选择",
|
||||
"select_profile": "选择要使用的{browser}配置文件:",
|
||||
"profile_list": "可用{browser}配置文件:",
|
||||
"default_profile": "默认配置文件",
|
||||
"profile": "配置文件 {number}",
|
||||
"no_profiles": "未找到Chrome配置文件",
|
||||
"error_loading": "加载Chrome配置文件时出错:{error}",
|
||||
"no_profiles": "未找到{browser}配置文件",
|
||||
"error_loading": "加载{browser}配置文件时出错:{error}",
|
||||
"profile_selected": "已选择配置文件:{profile}",
|
||||
"invalid_selection": "选择无效。请重试",
|
||||
"warning_chrome_close": "警告:这将关闭所有正在运行的Chrome进程"
|
||||
"invalid_selection": "选择无效。请重试"
|
||||
},
|
||||
"account_delete": {
|
||||
"title": "Cursor Google 账号删除工具",
|
||||
@@ -674,5 +697,50 @@
|
||||
"title": "Cursor 版本绕过工具",
|
||||
"description": "此工具修改 Cursor 的 product.json 以绕过版本限制",
|
||||
"menu_option": "绕过 Cursor 版本检查"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "检查授权...",
|
||||
"token_source": "从数据库获取 token 或手动输入?(d/m, 默认: d)",
|
||||
"getting_token_from_db": "从数据库获取 token...",
|
||||
"token_found_in_db": "在数据库中找到 token",
|
||||
"token_not_found_in_db": "在数据库中未找到 token",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
|
||||
"error_getting_token_from_db": "从数据库获取 token 时出错: {error}",
|
||||
"enter_token": "请输入您的 Cursor token: ",
|
||||
"token_length": "token 长度: {length}",
|
||||
"usage_response_status": "使用情况响应状态: {response}",
|
||||
"unexpected_status_code": "意外状态码: {code}",
|
||||
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 检查返回意外状态码。token 可能有效但 API 访问受限。",
|
||||
"invalid_token": "无效的 token",
|
||||
"user_authorized": "用户已授权",
|
||||
"user_unauthorized": "用户未授权",
|
||||
"request_timeout": "请求超时",
|
||||
"connection_error": "连接错误",
|
||||
"check_error": "检查授权时出错: {error}",
|
||||
"authorization_successful": "授权成功",
|
||||
"authorization_failed": "授权失败",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"unexpected_error": "意外错误: {error}",
|
||||
"error_generating_checksum": "生成校验和时出错: {error}",
|
||||
"checking_usage_information": "检查使用情况...",
|
||||
"check_usage_response": "检查使用情况响应: {response}",
|
||||
"usage_response": "使用情况响应: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "绕过 Token 限制工具",
|
||||
"description": "此工具修改 workbench.desktop.main.js 文件以绕过 token 限制",
|
||||
"press_enter": "按回车键继续..."
|
||||
},
|
||||
"token": {
|
||||
"refreshing": "正在刷新令牌...",
|
||||
"refresh_success": "令牌刷新成功!有效期 {days} 天(到期时间: {expire})",
|
||||
"no_access_token": "响应中没有访问令牌",
|
||||
"refresh_failed": "令牌刷新失败: {error}",
|
||||
"invalid_response": "刷新服务器返回无效的 JSON 响应",
|
||||
"server_error": "刷新服务器错误: HTTP {status}",
|
||||
"request_timeout": "刷新服务器请求超时",
|
||||
"connection_error": "连接刷新服务器错误",
|
||||
"unexpected_error": "令牌刷新过程中出现意外错误: {error}",
|
||||
"extraction_error": "提取令牌时出错: {error}"
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,9 @@
|
||||
"continue_prompt": "繼續?(y/N): ",
|
||||
"operation_cancelled_by_user": "操作被使用者取消",
|
||||
"exiting": "退出中 ……",
|
||||
"bypass_version_check": "繞過 Cursor 版本檢查"
|
||||
"bypass_version_check": "繞過 Cursor 版本檢查",
|
||||
"check_user_authorized": "檢查用戶授權",
|
||||
"bypass_token_limit": "繞過 Token 限制"
|
||||
},
|
||||
"languages": {
|
||||
"en": "英文",
|
||||
@@ -520,7 +522,9 @@
|
||||
"backup_created": "備份已創建: {path}",
|
||||
"config_removed": "配置文件已刪除用於強制更新",
|
||||
"backup_failed": "備份失敗: {error}",
|
||||
"force_update_failed": "強制更新配置失敗: {error}"
|
||||
"force_update_failed": "強制更新配置失敗: {error}",
|
||||
"config_force_update_disabled": "配置文件強制更新已禁用,跳過強制更新",
|
||||
"config_force_update_enabled": "配置文件強制更新已啟用,正在執行強制更新"
|
||||
},
|
||||
"oauth": {
|
||||
"authentication_button_not_found": "未找到認證按鈕",
|
||||
@@ -656,5 +660,38 @@
|
||||
"title": "Cursor 版本繞過工具",
|
||||
"description": "此工具修改 Cursor 的 product.json 以繞過版本限制",
|
||||
"menu_option": "繞過 Cursor 版本檢查"
|
||||
},
|
||||
"auth_check": {
|
||||
"checking_authorization": "檢查授權...",
|
||||
"token_source": "從資料庫獲取 token 或手動輸入?(d/m, 預設: d)",
|
||||
"getting_token_from_db": "從資料庫獲取 token...",
|
||||
"token_found_in_db": "在資料庫中找到 token",
|
||||
"token_not_found_in_db": "在資料庫中未找到 token",
|
||||
"cursor_acc_info_not_found": "cursor_acc_info.py 未找到",
|
||||
"usage_response_status": "使用情況響應狀態: {response}",
|
||||
"unexpected_status_code": "意外狀態碼: {code}",
|
||||
"jwt_token_warning": "token 似乎是 JWT 格式,但 API 檢查返回意外狀態碼。token 可能有效但 API 訪問受限。",
|
||||
"error_getting_token_from_db": "從資料庫獲取 token 時出錯: {error}",
|
||||
"enter_token": "請輸入您的 Cursor token: ",
|
||||
"token_length": "token 長度: {length}",
|
||||
"invalid_token": "無效的 token",
|
||||
"user_authorized": "用戶已授權",
|
||||
"user_unauthorized": "用戶未授權",
|
||||
"request_timeout": "請求超時",
|
||||
"connection_error": "連接錯誤",
|
||||
"check_error": "檢查授權時出錯: {error}",
|
||||
"authorization_successful": "授權成功",
|
||||
"authorization_failed": "授權失敗",
|
||||
"operation_cancelled": "操作已取消",
|
||||
"unexpected_error": "意外錯誤: {error}",
|
||||
"error_generating_checksum": "生成校驗和時出錯: {error}",
|
||||
"checking_usage_information": "檢查使用情況...",
|
||||
"check_usage_response": "檢查使用情況響應: {response}",
|
||||
"usage_response": "使用情況響應: {response}"
|
||||
},
|
||||
"bypass_token_limit": {
|
||||
"title": "繞過 Token 限制工具",
|
||||
"description": "此工具修改 workbench.desktop.main.js 文件以繞過 token 限制",
|
||||
"press_enter": "按回車鍵繼續..."
|
||||
}
|
||||
}
|
||||
14
main.py
14
main.py
@@ -287,7 +287,9 @@ def print_menu():
|
||||
12: f"{Fore.GREEN}12{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.config')}",
|
||||
13: f"{Fore.GREEN}13{Style.RESET_ALL}. {EMOJI['SETTINGS']} {translator.get('menu.select_chrome_profile')}",
|
||||
14: f"{Fore.GREEN}14{Style.RESET_ALL}. {EMOJI['ERROR']} {translator.get('menu.delete_google_account', fallback='Delete Cursor Google Account')}",
|
||||
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}"
|
||||
15: f"{Fore.GREEN}15{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_version_check', fallback='Bypass Cursor Version Check')}",
|
||||
16: f"{Fore.GREEN}16{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.check_user_authorized', fallback='Check User Authorized')}",
|
||||
17: f"{Fore.GREEN}17{Style.RESET_ALL}. {EMOJI['UPDATE']} {translator.get('menu.bypass_token_limit', fallback='Bypass Token Limit')}"
|
||||
}
|
||||
|
||||
# Automatically calculate the number of menu items in the left and right columns
|
||||
@@ -560,7 +562,7 @@ def main():
|
||||
|
||||
while True:
|
||||
try:
|
||||
choice_num = 15
|
||||
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":
|
||||
@@ -630,6 +632,14 @@ def main():
|
||||
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()
|
||||
|
||||
@@ -8,6 +8,7 @@ import configparser
|
||||
from pathlib import Path
|
||||
import sys
|
||||
from config import get_config
|
||||
from utils import get_default_browser_path as utils_get_default_browser_path
|
||||
|
||||
# Add global variable at the beginning of the file
|
||||
_translator = None
|
||||
@@ -112,29 +113,6 @@ def fill_signup_form(page, first_name, last_name, email, config, translator=None
|
||||
print(f"Error filling form: {e}")
|
||||
return False
|
||||
|
||||
def get_default_chrome_path():
|
||||
"""Get default Chrome path"""
|
||||
if sys.platform == "win32":
|
||||
paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Google/Chrome/Application/chrome.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Google/Chrome/Application/chrome.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google/Chrome/Application/chrome.exe')
|
||||
]
|
||||
elif sys.platform == "darwin":
|
||||
paths = [
|
||||
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
]
|
||||
else: # Linux
|
||||
paths = [
|
||||
"/usr/bin/google-chrome",
|
||||
"/usr/bin/google-chrome-stable"
|
||||
]
|
||||
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return ""
|
||||
|
||||
def get_user_documents_path():
|
||||
"""Get user Documents folder path"""
|
||||
if sys.platform == "win32":
|
||||
@@ -186,25 +164,32 @@ def setup_driver(translator=None):
|
||||
# Get config
|
||||
config = get_config(translator)
|
||||
|
||||
# Get Chrome path
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
|
||||
# Get browser type and path
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_path = config.get('Browser', f'{browser_type}_path', fallback=utils_get_default_browser_path(browser_type))
|
||||
|
||||
if not chrome_path or not os.path.exists(chrome_path):
|
||||
if not browser_path or not os.path.exists(browser_path):
|
||||
if translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {translator.get('register.chrome_path_invalid') if translator else 'Chrome路径无效,使用默认路径'}{Style.RESET_ALL}")
|
||||
chrome_path = get_default_chrome_path()
|
||||
print(f"{Fore.YELLOW}⚠️ {browser_type} {translator.get('register.browser_path_invalid')}{Style.RESET_ALL}")
|
||||
browser_path = utils_get_default_browser_path(browser_type)
|
||||
|
||||
# For backward compatibility, also check Chrome path
|
||||
if browser_type == 'chrome':
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=None)
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
browser_path = chrome_path
|
||||
|
||||
# Set browser options
|
||||
co = ChromiumOptions()
|
||||
|
||||
# Set Chrome path
|
||||
co.set_browser_path(chrome_path)
|
||||
# Set browser path
|
||||
co.set_browser_path(browser_path)
|
||||
|
||||
# Use incognito mode
|
||||
co.set_argument("--incognito")
|
||||
|
||||
if sys.platform == "linux":
|
||||
# Set random port
|
||||
# Set Linux specific options
|
||||
co.set_argument("--no-sandbox")
|
||||
|
||||
# Set random port
|
||||
@@ -213,6 +198,10 @@ def setup_driver(translator=None):
|
||||
# Use headless mode (must be set to False, simulate human operation)
|
||||
co.headless(False)
|
||||
|
||||
# Log browser info
|
||||
if translator:
|
||||
print(f"{Fore.CYAN}🌐 {translator.get('register.using_browser')}: {browser_type} {browser_path}{Style.RESET_ALL}")
|
||||
|
||||
try:
|
||||
# Load extension
|
||||
extension_path = os.path.join(os.getcwd(), "turnstilePatch")
|
||||
@@ -234,30 +223,38 @@ def setup_driver(translator=None):
|
||||
before_pids = []
|
||||
try:
|
||||
import psutil
|
||||
before_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
|
||||
browser_process_names = {
|
||||
'chrome': ['chrome', 'chromium'],
|
||||
'edge': ['msedge', 'edge'],
|
||||
'firefox': ['firefox'],
|
||||
'brave': ['brave', 'brave-browser']
|
||||
}
|
||||
process_names = browser_process_names.get(browser_type, ['chrome'])
|
||||
before_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
|
||||
except:
|
||||
pass
|
||||
|
||||
# Launch browser
|
||||
page = ChromiumPage(co)
|
||||
|
||||
# Wait a moment for Chrome to fully launch
|
||||
# Wait a moment for browser to fully launch
|
||||
time.sleep(1)
|
||||
|
||||
# Record Chrome processes after launching and find new ones
|
||||
# Record browser processes after launching and find new ones
|
||||
try:
|
||||
import psutil
|
||||
after_pids = [p.pid for p in psutil.process_iter() if 'chrome' in p.name().lower()]
|
||||
# Find new Chrome processes
|
||||
process_names = browser_process_names.get(browser_type, ['chrome'])
|
||||
after_pids = [p.pid for p in psutil.process_iter() if any(name in p.name().lower() for name in process_names)]
|
||||
# Find new browser processes
|
||||
new_pids = [pid for pid in after_pids if pid not in before_pids]
|
||||
_chrome_process_ids.extend(new_pids)
|
||||
|
||||
if _chrome_process_ids:
|
||||
print(f"Tracking {len(_chrome_process_ids)} Chrome processes")
|
||||
print(f"{translator.get('register.tracking_processes', count=len(_chrome_process_ids), browser=browser_type)}")
|
||||
else:
|
||||
print(f"{Fore.YELLOW}Warning: No new Chrome processes detected to track{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}Warning: {translator.get('register.no_new_processes_detected', browser=browser_type)}{Style.RESET_ALL}")
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not track Chrome processes: {e}")
|
||||
print(f"{translator.get('register.could_not_track_processes', browser=browser_type, error=str(e))}")
|
||||
|
||||
return config, page
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ from colorama import Fore, Style, init
|
||||
import requests
|
||||
import random
|
||||
import string
|
||||
from utils import get_random_wait_time
|
||||
from config import get_config
|
||||
from utils import get_random_wait_time, get_default_browser_path as utils_get_default_browser_path
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
@@ -104,9 +105,34 @@ class NewTempEmail:
|
||||
else:
|
||||
print(f"{Fore.CYAN}ℹ️ 正在启动浏览器...{Style.RESET_ALL}")
|
||||
|
||||
# 获取配置
|
||||
config = get_config(self.translator)
|
||||
|
||||
# 获取浏览器类型和路径
|
||||
browser_type = config.get('Browser', 'default_browser', fallback='chrome')
|
||||
browser_path = config.get('Browser', f'{browser_type}_path', fallback=utils_get_default_browser_path(browser_type))
|
||||
|
||||
if not browser_path or not os.path.exists(browser_path):
|
||||
if self.translator:
|
||||
print(f"{Fore.YELLOW}⚠️ {self.translator.get('email.browser_path_invalid', browser=browser_type) if self.translator else f'{browser_type} 路径无效,使用默认路径'}{Style.RESET_ALL}")
|
||||
browser_path = utils_get_default_browser_path(browser_type)
|
||||
|
||||
# 为了向后兼容,也检查 Chrome 路径
|
||||
if browser_type == 'chrome':
|
||||
chrome_path = config.get('Chrome', 'chromepath', fallback=None)
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
browser_path = chrome_path
|
||||
|
||||
# 创建浏览器选项
|
||||
co = ChromiumOptions()
|
||||
|
||||
# 设置浏览器路径
|
||||
co.set_browser_path(browser_path)
|
||||
|
||||
# 记录浏览器信息
|
||||
if self.translator:
|
||||
print(f"{Fore.CYAN}🌐 {self.translator.get('email.using_browser', browser=browser_type, path=browser_path) if self.translator else f'使用 {browser_type} 浏览器: {browser_path}'}{Style.RESET_ALL}")
|
||||
|
||||
# Only use headless for non-OAuth operations
|
||||
if not hasattr(self, 'auth_type') or self.auth_type != 'oauth':
|
||||
co.set_argument("--headless=new")
|
||||
@@ -122,22 +148,43 @@ class NewTempEmail:
|
||||
co.set_argument("--disable-dev-shm-usage")
|
||||
co.set_argument("--disable-gpu")
|
||||
|
||||
# If running as root, try to use actual user's Chrome profile
|
||||
# If running as root, try to use actual user's browser profile
|
||||
if os.geteuid() == 0:
|
||||
sudo_user = os.environ.get('SUDO_USER')
|
||||
if sudo_user:
|
||||
actual_home = f"/home/{sudo_user}"
|
||||
user_data_dir = os.path.join(actual_home, ".config", "google-chrome")
|
||||
|
||||
# 根据浏览器类型选择配置文件夹
|
||||
profile_dirs = {
|
||||
'chrome': os.path.join(actual_home, ".config", "google-chrome"),
|
||||
'brave': os.path.join(actual_home, ".config", "BraveSoftware", "Brave-Browser"),
|
||||
'edge': os.path.join(actual_home, ".config", "microsoft-edge"),
|
||||
'firefox': os.path.join(actual_home, ".mozilla", "firefox")
|
||||
}
|
||||
|
||||
user_data_dir = profile_dirs.get(browser_type, profile_dirs['chrome'])
|
||||
|
||||
if os.path.exists(user_data_dir):
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.using_chrome_profile', user_data_dir=user_data_dir) if self.translator else f'Using Chrome profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}ℹ️ {self.translator.get('email.using_browser_profile', browser=browser_type, user_data_dir=user_data_dir) if self.translator else f'Using {browser_type} profile from: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
co.set_argument(f"--user-data-dir={user_data_dir}")
|
||||
|
||||
co.auto_port() # 自动设置端口
|
||||
|
||||
# 根据浏览器类型设置扩展参数
|
||||
extension_args = {
|
||||
'chrome': "--allow-extensions-in-incognito",
|
||||
'brave': "--allow-extensions-in-brave-incognito", # Brave 可能使用不同的参数
|
||||
'edge': "--allow-extensions-in-incognito",
|
||||
'firefox': None # Firefox 可能使用不同的方式加载扩展
|
||||
}
|
||||
|
||||
extension_arg = extension_args.get(browser_type, "--allow-extensions-in-incognito")
|
||||
|
||||
# 加载 uBlock 插件
|
||||
try:
|
||||
extension_path = self.get_extension_block()
|
||||
co.set_argument("--allow-extensions-in-incognito")
|
||||
if extension_arg: # 如果有扩展参数
|
||||
co.set_argument(extension_arg)
|
||||
co.add_extension(extension_path)
|
||||
except Exception as e:
|
||||
if self.translator:
|
||||
@@ -154,8 +201,17 @@ class NewTempEmail:
|
||||
print(f"{Fore.RED}❌ 启动浏览器失败: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
if sys.platform == "linux":
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.make_sure_chrome_chromium_is_properly_installed') if self.translator else 'Make sure Chrome/Chromium is properly installed'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_install_chromium') if self.translator else 'Try: sudo apt install chromium-browser'}{Style.RESET_ALL}")
|
||||
browser_install_suggestions = {
|
||||
'chrome': "sudo apt install chromium-browser 或 sudo apt install google-chrome-stable",
|
||||
'brave': "sudo apt install brave-browser",
|
||||
'edge': "sudo apt install microsoft-edge-stable",
|
||||
'firefox': "sudo apt install firefox"
|
||||
}
|
||||
|
||||
suggestion = browser_install_suggestions.get(browser_type, browser_install_suggestions['chrome'])
|
||||
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.make_sure_browser_is_properly_installed', browser=browser_type) if self.translator else f'Make sure {browser_type} is properly installed'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}ℹ️ {self.translator.get('email.try_install_browser') if self.translator else f'Try: {suggestion}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
def create_email(self):
|
||||
|
||||
477
oauth_auth.py
477
oauth_auth.py
@@ -7,9 +7,10 @@ import sys
|
||||
import json
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from cursor_auth import CursorAuth
|
||||
from utils import get_random_wait_time, get_default_chrome_path
|
||||
from utils import get_random_wait_time, get_default_browser_path
|
||||
from config import get_config
|
||||
import platform
|
||||
from get_user_token import get_token_from_cookie
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
@@ -63,41 +64,100 @@ class OAuthHandler:
|
||||
return []
|
||||
|
||||
def _select_profile(self):
|
||||
"""Select a Chrome profile to use"""
|
||||
"""Allow user to select a browser profile to use"""
|
||||
try:
|
||||
# Get available profiles
|
||||
profiles = self._get_available_profiles(self._get_user_data_directory())
|
||||
if not profiles:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('chrome_profile.no_profiles') if self.translator else 'No Chrome profiles found'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# Display available profiles
|
||||
print(f"\n{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('chrome_profile.select_profile') if self.translator else 'Select a Chrome profile to use:'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{self.translator.get('chrome_profile.profile_list') if self.translator else 'Available profiles:'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}0. {self.translator.get('menu.exit') if self.translator else 'Exit'}{Style.RESET_ALL}")
|
||||
for i, (dir_name, display_name) in enumerate(profiles, 1):
|
||||
print(f"{Fore.CYAN}{i}. {display_name} ({dir_name}){Style.RESET_ALL}")
|
||||
|
||||
# Get user selection
|
||||
while True:
|
||||
try:
|
||||
choice = int(input(f"\n{Fore.CYAN}{self.translator.get('menu.input_choice', choices=f'0-{len(profiles)}') if self.translator else f'Please enter your choice (0-{len(profiles)}): '}{Style.RESET_ALL}"))
|
||||
if choice == 0: # Add quit
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.exiting') if self.translator else 'Exiting profile selection...'}{Style.RESET_ALL}")
|
||||
return False
|
||||
elif 1 <= choice <= len(profiles):
|
||||
self.selected_profile = profiles[choice - 1][0]
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('chrome_profile.profile_selected', profile=self.selected_profile) if self.translator else f'Selected profile: {self.selected_profile}'}{Style.RESET_ALL}")
|
||||
return True
|
||||
# 从配置中获取浏览器类型
|
||||
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):
|
||||
with open(local_state_file, 'r', encoding='utf-8') as f:
|
||||
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:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
|
||||
except ValueError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.invalid_selection') if self.translator else 'Invalid selection. Please try again.'}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}0. Exit{Style.RESET_ALL}")
|
||||
|
||||
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}")
|
||||
|
||||
try:
|
||||
choice = int(choice_str)
|
||||
if choice == 0:
|
||||
return False
|
||||
elif 1 <= choice <= max_choice:
|
||||
selected_profile = profiles[choice-1][0]
|
||||
self.selected_profile = selected_profile
|
||||
|
||||
if self.translator:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('browser_profile.profile_selected', profile=selected_profile)}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} Selected profile: {selected_profile}{Style.RESET_ALL}")
|
||||
return True
|
||||
else:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.invalid_selection')}{Style.RESET_ALL}")
|
||||
else:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} Invalid selection. Please try again.{Style.RESET_ALL}")
|
||||
return self._select_profile()
|
||||
except ValueError:
|
||||
if self.translator:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('browser_profile.invalid_selection')}{Style.RESET_ALL}")
|
||||
else:
|
||||
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
|
||||
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:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('chrome_profile.error_loading', error=str(e)) if self.translator else f'Error loading Chrome profiles: {e}'}{Style.RESET_ALL}")
|
||||
return False
|
||||
|
||||
# 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
|
||||
|
||||
def setup_browser(self):
|
||||
"""Setup browser for OAuth flow using selected profile"""
|
||||
try:
|
||||
@@ -107,11 +167,15 @@ class OAuthHandler:
|
||||
platform_name = platform.system().lower()
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.detected_platform', platform=platform_name) if self.translator else f'Detected platform: {platform_name}'}{Style.RESET_ALL}")
|
||||
|
||||
# 从配置中获取浏览器类型
|
||||
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()
|
||||
chrome_path = self._get_browser_path()
|
||||
browser_path = self._get_browser_path()
|
||||
|
||||
if not chrome_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" +
|
||||
@@ -119,8 +183,14 @@ class OAuthHandler:
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.found_browser_data_directory', path=user_data_dir) if self.translator else f'Found browser data directory: {user_data_dir}'}{Style.RESET_ALL}")
|
||||
|
||||
# Show warning about closing Chrome first
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {self.translator.get('chrome_profile.warning_chrome_close') if self.translator else 'Warning: This will close all running Chrome processes'}{Style.RESET_ALL}")
|
||||
# Show warning about closing browser first - 使用动态提示
|
||||
if self.translator:
|
||||
warning_msg = self.translator.get('oauth.warning_browser_close', browser=browser_type)
|
||||
else:
|
||||
warning_msg = f'Warning: This will close all running {browser_type} processes'
|
||||
|
||||
print(f"\n{Fore.YELLOW}{EMOJI['WARNING']} {warning_msg}{Style.RESET_ALL}")
|
||||
|
||||
choice = input(f"{Fore.YELLOW} {self.translator.get('menu.continue_prompt', choices='y/N')} {Style.RESET_ALL}").lower()
|
||||
if choice != 'y':
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('menu.operation_cancelled_by_user') if self.translator else 'Operation cancelled by user'}{Style.RESET_ALL}")
|
||||
@@ -135,14 +205,14 @@ class OAuthHandler:
|
||||
return False
|
||||
|
||||
# Configure browser options
|
||||
co = self._configure_browser_options(chrome_path, user_data_dir, self.selected_profile)
|
||||
co = self._configure_browser_options(browser_path, user_data_dir, self.selected_profile)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.starting_browser', path=chrome_path) if self.translator else f'Starting browser at: {chrome_path}'}{Style.RESET_ALL}")
|
||||
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') 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'}")
|
||||
|
||||
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
|
||||
@@ -158,14 +228,61 @@ class OAuthHandler:
|
||||
return False
|
||||
|
||||
def _kill_browser_processes(self):
|
||||
"""Kill existing browser processes based on platform"""
|
||||
"""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'],
|
||||
'linux': ['chrome', 'chromium', 'chromium-browser'],
|
||||
'mac': ['Chrome', 'Chromium']
|
||||
},
|
||||
'brave': {
|
||||
'win': ['brave.exe'],
|
||||
'linux': ['brave', 'brave-browser'],
|
||||
'mac': ['Brave Browser']
|
||||
},
|
||||
'edge': {
|
||||
'win': ['msedge.exe'],
|
||||
'linux': ['msedge'],
|
||||
'mac': ['Microsoft Edge']
|
||||
},
|
||||
'firefox': {
|
||||
'win': ['firefox.exe'],
|
||||
'linux': ['firefox'],
|
||||
'mac': ['Firefox']
|
||||
},
|
||||
'opera': {
|
||||
'win': ['opera.exe', 'launcher.exe'],
|
||||
'linux': ['opera'],
|
||||
'mac': ['Opera']
|
||||
}
|
||||
}
|
||||
|
||||
# 获取平台类型
|
||||
if os.name == 'nt':
|
||||
platform_type = 'win'
|
||||
elif sys.platform == 'darwin':
|
||||
platform_type = 'mac'
|
||||
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}")
|
||||
|
||||
# 根据平台关闭进程
|
||||
if os.name == 'nt': # Windows
|
||||
processes = ['chrome.exe', 'chromium.exe']
|
||||
for proc in processes:
|
||||
os.system(f'taskkill /f /im {proc} >nul 2>&1')
|
||||
else: # Linux/Mac
|
||||
processes = ['chrome', 'chromium', 'chromium-browser']
|
||||
for proc in processes:
|
||||
os.system(f'pkill -f {proc} >/dev/null 2>&1')
|
||||
|
||||
@@ -174,95 +291,182 @@ class OAuthHandler:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.warning_could_not_kill_existing_browser_processes', error=str(e)) if self.translator else f'Warning: Could not kill existing browser processes: {e}'}{Style.RESET_ALL}")
|
||||
|
||||
def _get_user_data_directory(self):
|
||||
"""Get the appropriate user data directory based on platform"""
|
||||
"""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
|
||||
possible_paths = [
|
||||
os.path.expandvars(r'%LOCALAPPDATA%\Google\Chrome\User Data'),
|
||||
os.path.expandvars(r'%LOCALAPPDATA%\Chromium\User Data')
|
||||
]
|
||||
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'),
|
||||
'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')
|
||||
}
|
||||
elif sys.platform == 'darwin': # macOS
|
||||
possible_paths = [
|
||||
os.path.expanduser('~/Library/Application Support/Google/Chrome'),
|
||||
os.path.expanduser('~/Library/Application Support/Chromium')
|
||||
]
|
||||
user_data_dirs = {
|
||||
'chrome': os.path.expanduser('~/Library/Application Support/Google/Chrome'),
|
||||
'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')
|
||||
}
|
||||
else: # Linux
|
||||
possible_paths = [
|
||||
os.path.expanduser('~/.config/google-chrome'),
|
||||
os.path.expanduser('~/.config/chromium'),
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/chromium-browser'
|
||||
]
|
||||
user_data_dirs = {
|
||||
'chrome': os.path.expanduser('~/.config/google-chrome'),
|
||||
'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')
|
||||
}
|
||||
|
||||
# Try each possible path
|
||||
for path in possible_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
# 获取选定浏览器的用户数据目录,如果找不到则使用 Chrome 的
|
||||
user_data_dir = user_data_dirs.get(browser_type)
|
||||
|
||||
# Create temporary profile if no existing profile found
|
||||
temp_profile = os.path.join(os.path.expanduser('~'), '.cursor_temp_profile')
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.creating_temporary_profile', path=temp_profile) if self.translator else f'Creating temporary profile at: {temp_profile}'}{Style.RESET_ALL}")
|
||||
os.makedirs(temp_profile, exist_ok=True)
|
||||
return temp_profile
|
||||
if user_data_dir and os.path.exists(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:
|
||||
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 目录
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_getting_user_data_directory', error=str(e)) if self.translator else f'Error getting user data directory: {e}'}{Style.RESET_ALL}")
|
||||
raise
|
||||
# 在出错时提供一个默认目录
|
||||
if os.name == 'nt':
|
||||
return os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Google', 'Chrome', 'User Data')
|
||||
elif sys.platform == 'darwin':
|
||||
return os.path.expanduser('~/Library/Application Support/Google/Chrome')
|
||||
else:
|
||||
return os.path.expanduser('~/.config/google-chrome')
|
||||
|
||||
def _get_browser_path(self):
|
||||
"""Get the browser executable path based on platform"""
|
||||
"""Get appropriate browser path based on platform and selected browser type"""
|
||||
try:
|
||||
# Try default path first
|
||||
chrome_path = get_default_chrome_path()
|
||||
if chrome_path and os.path.exists(chrome_path):
|
||||
return chrome_path
|
||||
# 从配置中获取浏览器类型
|
||||
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):
|
||||
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
|
||||
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('oauth.searching_for_alternative_browser_installations') if self.translator else 'Searching for alternative browser installations...'}{Style.RESET_ALL}")
|
||||
|
||||
# Platform-specific paths
|
||||
# 如果未找到配置中指定的浏览器,则尝试查找其他兼容浏览器
|
||||
if os.name == 'nt': # Windows
|
||||
alt_paths = [
|
||||
r'C:\Program Files\Google\Chrome\Application\chrome.exe',
|
||||
r'C:\Program Files (x86)\Google\Chrome\Application\chrome.exe',
|
||||
r'C:\Program Files\Chromium\Application\chrome.exe',
|
||||
os.path.expandvars(r'%ProgramFiles%\Google\Chrome\Application\chrome.exe'),
|
||||
os.path.expandvars(r'%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe')
|
||||
]
|
||||
possible_paths = []
|
||||
if browser_type == 'brave':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware', 'Brave-Browser', 'Application', 'brave.exe')
|
||||
]
|
||||
elif browser_type == 'edge':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Microsoft', 'Edge', 'Application', 'msedge.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Microsoft', 'Edge', 'Application', 'msedge.exe')
|
||||
]
|
||||
elif browser_type == 'firefox':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Mozilla Firefox', 'firefox.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'Mozilla Firefox', 'firefox.exe')
|
||||
]
|
||||
elif browser_type == 'opera':
|
||||
possible_paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'Opera', 'opera.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(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')
|
||||
]
|
||||
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': # macOS
|
||||
alt_paths = [
|
||||
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||
'/Applications/Chromium.app/Contents/MacOS/Chromium',
|
||||
'~/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
|
||||
'~/Applications/Chromium.app/Contents/MacOS/Chromium'
|
||||
]
|
||||
possible_paths = []
|
||||
if browser_type == 'brave':
|
||||
possible_paths = ['/Applications/Brave Browser.app/Contents/MacOS/Brave Browser']
|
||||
elif browser_type == 'edge':
|
||||
possible_paths = ['/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge']
|
||||
elif browser_type == 'firefox':
|
||||
possible_paths = ['/Applications/Firefox.app/Contents/MacOS/firefox']
|
||||
else: # 默认为 Chrome
|
||||
possible_paths = ['/Applications/Google Chrome.app/Contents/MacOS/Google Chrome']
|
||||
|
||||
else: # Linux
|
||||
alt_paths = [
|
||||
'/usr/bin/google-chrome',
|
||||
'/usr/bin/chromium-browser',
|
||||
'/usr/bin/chromium',
|
||||
'/snap/bin/chromium',
|
||||
'/usr/local/bin/chrome',
|
||||
'/usr/local/bin/chromium'
|
||||
]
|
||||
possible_paths = []
|
||||
if browser_type == 'brave':
|
||||
possible_paths = ['/usr/bin/brave-browser', '/usr/bin/brave']
|
||||
elif browser_type == 'edge':
|
||||
possible_paths = ['/usr/bin/microsoft-edge']
|
||||
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']
|
||||
|
||||
# 检查每个可能的路径
|
||||
for path in possible_paths:
|
||||
if os.path.exists(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
|
||||
|
||||
# Try each alternative path
|
||||
for path in alt_paths:
|
||||
expanded_path = os.path.expanduser(path)
|
||||
if os.path.exists(expanded_path):
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.found_browser_at', path=expanded_path) if self.translator else f'Found browser at: {expanded_path}'}{Style.RESET_ALL}")
|
||||
return expanded_path
|
||||
# 如果找不到指定浏览器,则尝试使用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()
|
||||
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.error_finding_browser_path', error=str(e)) if self.translator else f'Error finding browser path: {e}'}{Style.RESET_ALL}")
|
||||
return None
|
||||
|
||||
def _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, chrome_path, user_data_dir, active_profile):
|
||||
def _configure_browser_options(self, browser_path, user_data_dir, active_profile):
|
||||
"""Configure browser options based on platform"""
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_paths(browser_path=chrome_path, user_data_path=user_data_dir)
|
||||
co.set_paths(browser_path=browser_path, user_data_path=user_data_dir)
|
||||
co.set_argument(f'--profile-directory={active_profile}')
|
||||
|
||||
# Basic options
|
||||
@@ -330,13 +534,19 @@ class OAuthHandler:
|
||||
# Check if we're on account selection page
|
||||
if "accounts.google.com" in self.browser.url:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue...'}{Style.RESET_ALL}")
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except:
|
||||
pass # Alert is optional
|
||||
|
||||
# 获取配置中是否启用 alert 选项
|
||||
config = get_config(self.translator)
|
||||
show_alert = config.getboolean('OAuth', 'show_selection_alert', fallback=False)
|
||||
|
||||
if show_alert:
|
||||
alert_message = self.translator.get('oauth.please_select_your_google_account_to_continue') if self.translator else 'Please select your Google account to continue with Cursor authentication'
|
||||
try:
|
||||
self.browser.run_js(f"""
|
||||
alert('{alert_message}');
|
||||
""")
|
||||
except:
|
||||
pass # Alert is optional
|
||||
|
||||
# Wait for authentication to complete
|
||||
auth_info = self._wait_for_auth()
|
||||
@@ -378,13 +588,7 @@ class OAuthHandler:
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
token = None
|
||||
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
|
||||
token = get_token_from_cookie(value, self.translator)
|
||||
if token:
|
||||
# Get email from settings page
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {self.translator.get('oauth.authentication_successful_getting_account_info') if self.translator else 'Authentication successful, getting account info...'}{Style.RESET_ALL}")
|
||||
@@ -420,7 +624,6 @@ class OAuthHandler:
|
||||
|
||||
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":
|
||||
@@ -459,7 +662,7 @@ class OAuthHandler:
|
||||
|
||||
# Setup browser
|
||||
if not self.setup_browser():
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('oauth.browser_failed')}{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
|
||||
|
||||
# Navigate to auth URL
|
||||
@@ -590,11 +793,7 @@ class OAuthHandler:
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
|
||||
token = get_token_from_cookie(value, self.translator)
|
||||
if token:
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('oauth.authentication_successful') if self.translator else 'Authentication successful!'}{Style.RESET_ALL}")
|
||||
# Navigate to settings page
|
||||
@@ -632,7 +831,6 @@ class OAuthHandler:
|
||||
|
||||
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":
|
||||
@@ -658,10 +856,7 @@ class OAuthHandler:
|
||||
for cookie in cookies:
|
||||
if cookie.get("name") == "WorkosCursorSessionToken":
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
token = get_token_from_cookie(value, self.translator)
|
||||
if token:
|
||||
# Get email and check usage here too
|
||||
try:
|
||||
@@ -691,19 +886,18 @@ class OAuthHandler:
|
||||
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()
|
||||
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:
|
||||
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}")
|
||||
return self.handle_github_auth()
|
||||
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.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}")
|
||||
|
||||
@@ -759,10 +953,7 @@ class OAuthHandler:
|
||||
if name == "WorkosCursorSessionToken":
|
||||
try:
|
||||
value = cookie.get("value", "")
|
||||
if "::" in value:
|
||||
token = value.split("::")[-1]
|
||||
elif "%3A%3A" in value:
|
||||
token = value.split("%3A%3A")[-1]
|
||||
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}")
|
||||
elif name == "cursor_email":
|
||||
|
||||
@@ -15,6 +15,7 @@ import configparser
|
||||
from new_signup import get_user_documents_path
|
||||
import traceback
|
||||
from config import get_config
|
||||
from datetime import datetime
|
||||
|
||||
# Initialize colorama
|
||||
init()
|
||||
@@ -220,8 +221,12 @@ def get_workbench_cursor_path(translator=None) -> str:
|
||||
|
||||
if system == "Windows":
|
||||
base_path = config.get('WindowsPaths', 'cursor_path')
|
||||
else:
|
||||
elif system == "Darwin":
|
||||
base_path = paths_map[system]["base"]
|
||||
else: # Linux
|
||||
# For Linux, we've already checked all bases in the loop above
|
||||
# If we're here, it means none of the bases worked, so we'll use the first one
|
||||
base_path = paths_map[system]["bases"][0]
|
||||
|
||||
main_path = os.path.join(base_path, paths_map[system]["main"])
|
||||
|
||||
@@ -334,37 +339,40 @@ def modify_workbench_js(file_path: str, translator=None) -> bool:
|
||||
with open(file_path, "r", encoding="utf-8", errors="ignore") as main_file:
|
||||
content = main_file.read()
|
||||
|
||||
if sys.platform == "win32":
|
||||
# Define replacement patterns
|
||||
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
elif sys.platform == "linux":
|
||||
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
elif sys.platform == "darwin":
|
||||
CButton_old_pattern = r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)'
|
||||
CButton_new_pattern = r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)'
|
||||
patterns = {
|
||||
# 通用按钮替换模式
|
||||
r'B(k,D(Ln,{title:"Upgrade to Pro",size:"small",get codicon(){return A.rocket},get onClick(){return t.pay}}),null)': r'B(k,D(Ln,{title:"yeongpin GitHub",size:"small",get codicon(){return A.github},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Windows/Linux/Mac 通用按钮替换模式
|
||||
r'M(x,I(as,{title:"Upgrade to Pro",size:"small",get codicon(){return $.rocket},get onClick(){return t.pay}}),null)': r'M(x,I(as,{title:"yeongpin GitHub",size:"small",get codicon(){return $.rocket},get onClick(){return function(){window.open("https://github.com/yeongpin/cursor-free-vip","_blank")}}}),null)',
|
||||
|
||||
# Badge 替换
|
||||
r'<div>Pro Trial': r'<div>Pro',
|
||||
|
||||
CBadge_old_pattern = r'<div>Pro Trial'
|
||||
CBadge_new_pattern = r'<div>Pro'
|
||||
r'py-1">Auto-select': r'py-1">Bypass-Version-Pin',
|
||||
|
||||
#
|
||||
r'async getEffectiveTokenLimit(e){const n=e.modelName;if(!n)return 2e5;':r'async getEffectiveTokenLimit(e){return 9000000;const n=e.modelName;if(!n)return 9e5;',
|
||||
# Pro
|
||||
r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>.");': r'var DWr=ne("<div class=settings__item_description>You are currently signed in with <strong></strong>. <h1>Pro</h1>");',
|
||||
|
||||
# Toast 替换
|
||||
r'notifications-toasts': r'notifications-toasts hidden'
|
||||
}
|
||||
|
||||
CToast_old_pattern = r'notifications-toasts'
|
||||
CToast_new_pattern = r'notifications-toasts hidden'
|
||||
|
||||
# Replace content
|
||||
content = content.replace(CButton_old_pattern, CButton_new_pattern)
|
||||
content = content.replace(CBadge_old_pattern, CBadge_new_pattern)
|
||||
content = content.replace(CToast_old_pattern, CToast_new_pattern)
|
||||
# 使用patterns进行替换
|
||||
for old_pattern, new_pattern in patterns.items():
|
||||
content = content.replace(old_pattern, new_pattern)
|
||||
|
||||
# Write to temporary file
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
# Backup original file
|
||||
backup_path = file_path + ".backup"
|
||||
if os.path.exists(backup_path):
|
||||
os.remove(backup_path)
|
||||
# Backup original file with timestamp
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{file_path}.backup.{timestamp}"
|
||||
shutil.copy2(file_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
|
||||
# Move temporary file to original position
|
||||
if os.path.exists(file_path):
|
||||
@@ -411,7 +419,10 @@ def modify_main_js(main_path: str, translator) -> bool:
|
||||
tmp_file.write(content)
|
||||
tmp_path = tmp_file.name
|
||||
|
||||
shutil.copy2(main_path, main_path + ".old")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{main_path}.old.{timestamp}"
|
||||
shutil.copy2(main_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
shutil.move(tmp_path, main_path)
|
||||
|
||||
os.chmod(main_path, original_mode)
|
||||
@@ -461,7 +472,8 @@ def patch_cursor_get_machine_id(translator) -> bool:
|
||||
print(f"{Fore.CYAN}{EMOJI['INFO']} {translator.get('reset.version_check_passed')}{Style.RESET_ALL}")
|
||||
|
||||
# Backup file
|
||||
backup_path = main_path + ".bak"
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{main_path}.bak.{timestamp}"
|
||||
if not os.path.exists(backup_path):
|
||||
shutil.copy2(main_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {translator.get('reset.backup_created', path=backup_path)}{Style.RESET_ALL}")
|
||||
@@ -638,8 +650,8 @@ class MachineIDResetter:
|
||||
winreg.SetValueEx(key, "MachineGuid", 0, winreg.REG_SZ, new_guid)
|
||||
winreg.CloseKey(key)
|
||||
print(f"{Fore.GREEN}{EMOJI['SUCCESS']} {self.translator.get('reset.windows_machine_guid_updated')}{Style.RESET_ALL}")
|
||||
except PermissionError:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied')}{Style.RESET_ALL}")
|
||||
except PermissionError as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.permission_denied', error=str(e))}{Style.RESET_ALL}")
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('reset.update_windows_machine_guid_failed', error=str(e))}{Style.RESET_ALL}")
|
||||
@@ -717,12 +729,10 @@ class MachineIDResetter:
|
||||
with open(self.db_path, "r", encoding="utf-8") as f:
|
||||
config = json.load(f)
|
||||
|
||||
backup_path = self.db_path + ".bak"
|
||||
if not os.path.exists(backup_path):
|
||||
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
else:
|
||||
print(f"{Fore.YELLOW}{EMOJI['INFO']} {self.translator.get('reset.backup_exists')}{Style.RESET_ALL}")
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{self.db_path}.bak.{timestamp}"
|
||||
print(f"{Fore.YELLOW}{EMOJI['BACKUP']} {self.translator.get('reset.creating_backup')}: {backup_path}{Style.RESET_ALL}")
|
||||
shutil.copy2(self.db_path, backup_path)
|
||||
|
||||
print(f"{Fore.CYAN}{EMOJI['RESET']} {self.translator.get('reset.generating')}...{Style.RESET_ALL}")
|
||||
new_ids = self.generate_new_ids()
|
||||
@@ -786,7 +796,8 @@ class MachineIDResetter:
|
||||
|
||||
# Create backup if file exists
|
||||
if os.path.exists(machine_id_path):
|
||||
backup_path = machine_id_path + ".backup"
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
backup_path = f"{machine_id_path}.backup.{timestamp}"
|
||||
try:
|
||||
shutil.copy2(machine_id_path, backup_path)
|
||||
print(f"{Fore.GREEN}{EMOJI['INFO']} {self.translator.get('reset.backup_created', path=backup_path) if self.translator else f'Backup created at: {backup_path}'}{Style.RESET_ALL}")
|
||||
|
||||
157
utils.py
157
utils.py
@@ -9,24 +9,147 @@ def get_user_documents_path():
|
||||
return os.path.expanduser("~\\Documents")
|
||||
else:
|
||||
return os.path.expanduser("~/Documents")
|
||||
|
||||
def get_default_chrome_path():
|
||||
"""Get default Chrome path"""
|
||||
if sys.platform == "win32":
|
||||
# Trying to find chrome in PATH
|
||||
try:
|
||||
import shutil
|
||||
chrome_in_path = shutil.which("chrome")
|
||||
if chrome_in_path:
|
||||
return chrome_in_path
|
||||
except:
|
||||
pass
|
||||
# Going to default path
|
||||
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
|
||||
elif sys.platform == "darwin":
|
||||
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
|
||||
def get_default_driver_path(browser_type='chrome'):
|
||||
"""Get default driver path based on browser type"""
|
||||
browser_type = browser_type.lower()
|
||||
if browser_type == 'chrome':
|
||||
return get_default_chrome_driver_path()
|
||||
elif browser_type == 'edge':
|
||||
return get_default_edge_driver_path()
|
||||
elif browser_type == 'firefox':
|
||||
return get_default_firefox_driver_path()
|
||||
elif browser_type == 'brave':
|
||||
# Brave 使用 Chrome 的 driver
|
||||
return get_default_chrome_driver_path()
|
||||
else:
|
||||
return "/usr/bin/google-chrome"
|
||||
# Default to Chrome if browser type is unknown
|
||||
return get_default_chrome_driver_path()
|
||||
|
||||
def get_default_chrome_driver_path():
|
||||
"""Get default Chrome driver path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver.exe")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "chromedriver")
|
||||
else:
|
||||
return "/usr/local/bin/chromedriver"
|
||||
|
||||
def get_default_edge_driver_path():
|
||||
"""Get default Edge driver path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver.exe")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "msedgedriver")
|
||||
else:
|
||||
return "/usr/local/bin/msedgedriver"
|
||||
|
||||
def get_default_firefox_driver_path():
|
||||
"""Get default Firefox driver path"""
|
||||
if sys.platform == "win32":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver.exe")
|
||||
elif sys.platform == "darwin":
|
||||
return os.path.join(os.path.dirname(os.path.abspath(__file__)), "drivers", "geckodriver")
|
||||
else:
|
||||
return "/usr/local/bin/geckodriver"
|
||||
|
||||
def get_default_brave_driver_path():
|
||||
"""Get default Brave driver path (uses Chrome driver)"""
|
||||
# Brave 浏览器基于 Chromium,所以使用相同的 chromedriver
|
||||
return get_default_chrome_driver_path()
|
||||
|
||||
def get_default_browser_path(browser_type='chrome'):
|
||||
"""Get default browser executable path"""
|
||||
browser_type = browser_type.lower()
|
||||
|
||||
if sys.platform == "win32":
|
||||
if browser_type == 'chrome':
|
||||
# 尝试在 PATH 中找到 Chrome
|
||||
try:
|
||||
import shutil
|
||||
chrome_in_path = shutil.which("chrome")
|
||||
if chrome_in_path:
|
||||
return chrome_in_path
|
||||
except:
|
||||
pass
|
||||
# 使用默认路径
|
||||
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
|
||||
elif browser_type == 'edge':
|
||||
return r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
|
||||
elif browser_type == 'firefox':
|
||||
return r"C:\Program Files\Mozilla Firefox\firefox.exe"
|
||||
elif browser_type == 'opera':
|
||||
# 尝试多个可能的 Opera 路径
|
||||
opera_paths = [
|
||||
r"C:\Program Files\Opera\opera.exe",
|
||||
r"C:\Program Files (x86)\Opera\opera.exe",
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'launcher.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera', 'opera.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'launcher.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'Programs', 'Opera GX', 'opera.exe')
|
||||
]
|
||||
for path in opera_paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return opera_paths[0] # 返回第一个路径,即使它不存在
|
||||
elif browser_type == 'brave':
|
||||
# Brave 浏览器的默认安装路径
|
||||
paths = [
|
||||
os.path.join(os.environ.get('PROGRAMFILES', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
|
||||
os.path.join(os.environ.get('PROGRAMFILES(X86)', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe'),
|
||||
os.path.join(os.environ.get('LOCALAPPDATA', ''), 'BraveSoftware/Brave-Browser/Application/brave.exe')
|
||||
]
|
||||
for path in paths:
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return paths[0] # 返回第一个路径,即使它不存在
|
||||
|
||||
elif sys.platform == "darwin":
|
||||
if browser_type == 'chrome':
|
||||
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
|
||||
elif browser_type == 'edge':
|
||||
return "/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge"
|
||||
elif browser_type == 'firefox':
|
||||
return "/Applications/Firefox.app/Contents/MacOS/firefox"
|
||||
elif browser_type == 'brave':
|
||||
return "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
|
||||
elif browser_type == 'opera':
|
||||
return "/Applications/Opera.app/Contents/MacOS/Opera"
|
||||
|
||||
else: # Linux
|
||||
if browser_type == 'chrome':
|
||||
# 尝试多种可能的名称
|
||||
chrome_names = ["google-chrome", "chrome", "chromium", "chromium-browser"]
|
||||
for name in chrome_names:
|
||||
try:
|
||||
import shutil
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
return path
|
||||
except:
|
||||
pass
|
||||
return "/usr/bin/google-chrome"
|
||||
elif browser_type == 'edge':
|
||||
return "/usr/bin/microsoft-edge"
|
||||
elif browser_type == 'firefox':
|
||||
return "/usr/bin/firefox"
|
||||
elif browser_type == 'opera':
|
||||
return "/usr/bin/opera"
|
||||
elif browser_type == 'brave':
|
||||
# 尝试常见的 Brave 路径
|
||||
brave_names = ["brave", "brave-browser"]
|
||||
for name in brave_names:
|
||||
try:
|
||||
import shutil
|
||||
path = shutil.which(name)
|
||||
if path:
|
||||
return path
|
||||
except:
|
||||
pass
|
||||
return "/usr/bin/brave-browser"
|
||||
|
||||
# 如果找不到指定的浏览器类型,则返回 Chrome 的路径
|
||||
return get_default_browser_path('chrome')
|
||||
|
||||
def get_linux_cursor_path():
|
||||
"""Get Linux Cursor path"""
|
||||
|
||||
Reference in New Issue
Block a user