feat: Refactor Project Structure and Add Configuration Management

- Add `config.py` for centralized configuration management
- Add `utils.py` for cross-platform utility functions
- Remove `browser.py` and `control.py` to simplify project structure
- Update version to 1.7.06
- Modify build specification to reflect new file structure
- Update localization files with new update confirmation messages
- Enhance configuration loading and path detection across different platforms
This commit is contained in:
yeongpin 2025-03-11 11:49:17 +08:00
parent 6ca80ccb10
commit ff358588bb
18 changed files with 303 additions and 484 deletions

4
.env
View File

@ -1,2 +1,2 @@
version=1.7.05
VERSION=1.7.05
version=1.7.06
VERSION=1.7.06

View File

@ -6,7 +6,7 @@ on:
version:
description: 'Version number (e.g. 1.0.9)'
required: true
default: '1.7.05'
default: '1.7.06'
permissions:
contents: write

View File

@ -1,5 +1,19 @@
# Change Log
## v1.7.06
1. Add: Update Confirm | 增加更新確認
2. Add: Update Skipped | 增加更新跳過
3. Add: Invalid Choice | 增加無效選擇
4. Fix: Cursor Path | 修復Cursor路徑
5. Fix: Path Encoding | 修復路徑編碼
6. Fix: Getting Verification Code | 修復獲取驗證碼
7. Fix: Setting Password | 修復設置密碼
8. Fix: Disable Auto Update | 修復禁用自動更新
9. Add Config.py | 增加Config.py
10. Add utils.py | 增加utils.py
11. Rebuild some logic | 重新構建一些邏輯
## v1.7.05
1. Fix: Cursor Version Check | 修復Cursor版本檢查
2. Fix: Small Problem | 修復一些小問題

View File

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

View File

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

117
config.py Normal file
View File

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

View File

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

View File

@ -2,6 +2,7 @@ import sqlite3
import os
import sys
from colorama import Fore, Style, init
from config import get_config
# 初始化colorama
init()
@ -21,21 +22,40 @@ EMOJI = {
class CursorAuth:
def __init__(self, translator=None):
self.translator = translator
# 判断操作系统
# 获取配置
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.config_error') if self.translator else 'Failed to load configuration'}{Style.RESET_ALL}")
sys.exit(1)
# 根据操作系统获取路径
try:
if sys.platform == "win32": # Windows
self.db_path = os.path.join(
os.getenv("APPDATA"), "Cursor", "User", "globalStorage", "state.vscdb"
)
elif sys.platform == 'linux':
self.db_path = os.path.expanduser(
"~/.config/Cursor/User/globalStorage/state.vscdb"
)
if not config.has_section('WindowsPaths'):
raise ValueError("Windows paths not configured")
self.db_path = config.get('WindowsPaths', 'sqlite_path')
elif sys.platform == 'linux': # Linux
if not config.has_section('LinuxPaths'):
raise ValueError("Linux paths not configured")
self.db_path = config.get('LinuxPaths', 'sqlite_path')
elif sys.platform == 'darwin': # macOS
self.db_path = os.path.expanduser(
"~/Library/Application Support/Cursor/User/globalStorage/state.vscdb"
)
if not config.has_section('MacPaths'):
raise ValueError("macOS paths not configured")
self.db_path = config.get('MacPaths', 'sqlite_path')
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform')}{Style.RESET_ALL}")
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.unsupported_platform') if self.translator else 'Unsupported platform'}{Style.RESET_ALL}")
sys.exit(1)
# 验证路径是否存在
if not os.path.exists(os.path.dirname(self.db_path)):
raise FileNotFoundError(f"Database directory not found: {os.path.dirname(self.db_path)}")
except Exception as e:
print(f"{Fore.RED}{EMOJI['ERROR']} {self.translator.get('auth.path_error', error=str(e)) if self.translator else f'Error getting database path: {str(e)}'}{Style.RESET_ALL}")
sys.exit(1)
# 检查数据库文件是否存在

View File

@ -2,8 +2,6 @@ import os
from colorama import Fore, Style, init
import time
import random
from browser import BrowserManager
from control import BrowserControl
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
@ -35,7 +33,6 @@ class CursorRegistration:
self.translator = translator
# Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager()
self.browser = None
self.controller = None
self.mail_url = "https://yopmail.com/zh/email-generator"

View File

@ -2,8 +2,6 @@ import os
from colorama import Fore, Style, init
import time
import random
from browser import BrowserManager
from control import BrowserControl
from cursor_auth import CursorAuth
from reset_machine_manual import MachineIDResetter
@ -35,7 +33,6 @@ class CursorRegistration:
self.translator = translator
# Set to display mode
os.environ['BROWSER_HEADLESS'] = 'False'
self.browser_manager = BrowserManager()
self.browser = None
self.controller = None
self.sign_up_url = "https://authenticator.cursor.sh/sign-up"

View File

@ -4,6 +4,7 @@ import platform
import shutil
from colorama import Fore, Style, init
import subprocess
from config import get_config
# Initialize colorama
init()
@ -24,11 +25,24 @@ class AutoUpdateDisabler:
def __init__(self, translator=None):
self.translator = translator
self.system = platform.system()
# 从配置文件获取路径
config = get_config(translator)
if config:
if self.system == "Windows":
self.updater_path = config.get('WindowsPaths', 'updater_path', fallback=os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"))
elif self.system == "Darwin":
self.updater_path = config.get('MacPaths', 'updater_path', fallback=os.path.expanduser("~/Library/Application Support/cursor-updater"))
elif self.system == "Linux":
self.updater_path = config.get('LinuxPaths', 'updater_path', fallback=os.path.expanduser("~/.config/cursor-updater"))
else:
# 如果配置加载失败,使用默认路径
self.updater_paths = {
"Windows": os.path.join(os.getenv("LOCALAPPDATA", ""), "cursor-updater"),
"Darwin": os.path.expanduser("~/Library/Application Support/cursor-updater"),
"Linux": os.path.expanduser("~/.config/cursor-updater")
}
self.updater_path = self.updater_paths.get(self.system)
def _kill_cursor_processes(self):
"""End all Cursor processes"""
@ -50,7 +64,7 @@ class AutoUpdateDisabler:
def _remove_updater_directory(self):
"""Delete updater directory"""
try:
updater_path = self.updater_paths.get(self.system)
updater_path = self.updater_path
if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")
@ -72,7 +86,7 @@ class AutoUpdateDisabler:
def _create_blocking_file(self):
"""Create blocking file"""
try:
updater_path = self.updater_paths.get(self.system)
updater_path = self.updater_path
if not updater_path:
raise OSError(self.translator.get('update.unsupported_os', system=self.system) if self.translator else f"不支持的操作系统: {self.system}")

View File

@ -160,7 +160,9 @@
"config_option_added": "Config Option Added: {option}",
"config_updated": "Config Updated",
"password_submitted": "Password Submitted",
"total_usage": "Total Usage: {usage}"
"total_usage": "Total Usage: {usage}",
"setting_on_password": "Setting Password",
"getting_code": "Getting Verification Code, Will Try in 60s"
},
"auth": {
"title": "Cursor Auth Manager",
@ -270,6 +272,9 @@
"updating": "Updating to the latest version. The program will restart automatically.",
"up_to_date": "You are using the latest version.",
"check_failed": "Failed to check for updates: {error}",
"continue_anyway": "Continuing with current version..."
"continue_anyway": "Continuing with current version...",
"update_confirm": "Do you want to update to the latest version? (Y/n)",
"update_skipped": "Skipping update.",
"invalid_choice": "Invalid choice. Please enter 'Y' or 'n'."
}
}

View File

@ -159,7 +159,9 @@
"config_option_added": "配置项已添加: {option}",
"config_updated": "配置已更新",
"password_submitted": "密码已提交",
"total_usage": "总使用量: {usage}"
"total_usage": "总使用量: {usage}",
"setting_on_password": "设置密码",
"getting_code": "获取验证码将在60秒内尝试..."
},
"auth": {
"title": "Cursor 认证管理器",
@ -266,6 +268,9 @@
"updating": "正在更新到最新版本。程序将自动重启。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "检查更新失败: {error}",
"continue_anyway": "继续使用当前版本..."
"continue_anyway": "继续使用当前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳过更新。",
"invalid_choice": "选择无效。请输入 'Y' 或 'n'."
}
}

View File

@ -141,7 +141,9 @@
"config_option_added": "配置項已添加: {option}",
"config_updated": "配置已更新",
"password_submitted": "密碼已提交",
"total_usage": "總使用量: {usage}"
"total_usage": "總使用量: {usage}",
"setting_on_password": "設置密碼",
"getting_code": "正在獲取驗證碼將在60秒內嘗試..."
},
"auth": {
"title": "Cursor 認證管理器",
@ -248,6 +250,9 @@
"updating": "正在更新到最新版本。程序將自動重啟。",
"up_to_date": "您使用的是最新版本。",
"check_failed": "檢查更新失敗: {error}",
"continue_anyway": "繼續使用當前版本..."
"continue_anyway": "繼續使用當前版本...",
"update_confirm": "是否要更新到最新版本? (Y/n)",
"update_skipped": "跳過更新。",
"invalid_choice": "選擇無效。請輸入 'Y' 或 'n'."
}
}

15
main.py
View File

@ -9,6 +9,7 @@ import locale
import platform
import requests
import subprocess
from config import get_config
# 只在 Windows 系统上导入 windll
if platform.system() == 'Windows':
@ -238,6 +239,17 @@ def check_latest_version():
if latest_version != version:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.new_version_available', current=version, latest=latest_version)}{Style.RESET_ALL}")
# 詢問用戶是否要更新
while True:
choice = input(f"\n{EMOJI['ARROW']} {Fore.CYAN}{translator.get('updater.update_confirm', choices='Y/n')}: {Style.RESET_ALL}").lower()
if choice in ['', 'y', 'yes']:
break
elif choice in ['n', 'no']:
print(f"\n{Fore.YELLOW}{EMOJI['INFO']} {translator.get('updater.update_skipped')}{Style.RESET_ALL}")
return
else:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.invalid_choice')}{Style.RESET_ALL}")
try:
# Execute update command based on platform
if platform.system() == 'Windows':
@ -287,8 +299,7 @@ def main():
print_logo()
# 初始化配置
from new_signup import setup_config
config = setup_config(translator)
config = get_config(translator)
if not config:
print(f"{Fore.RED}{EMOJI['ERROR']} {translator.get('menu.config_init_failed')}{Style.RESET_ALL}")
return

View File

@ -7,6 +7,7 @@ from colorama import Fore, Style
import configparser
from pathlib import Path
import sys
from config import get_config
# 在文件开头添加全局变量
_translator = None
@ -161,120 +162,11 @@ def get_random_wait_time(config, timing_type='page_load_wait'):
except:
return random.uniform(0.1, 0.8) # 出错时返回默认值
def setup_config(translator=None):
"""Setup configuration file and return config object"""
try:
# Set configuration file path
config_dir = os.path.join(get_user_documents_path(), ".cursor-free-vip")
config_file = os.path.join(config_dir, "config.ini")
# Create config directory (if it doesn't exist)
os.makedirs(config_dir, exist_ok=True)
# Read or create configuration file
config = configparser.ConfigParser()
# 默认配置
default_config = {
'Chrome': {
'chromepath': get_default_chrome_path()
},
'Turnstile': {
'handle_turnstile_time': '2',
'handle_turnstile_random_time': '1-3'
},
'Timing': {
'min_random_time': '0.1',
'max_random_time': '0.8',
'page_load_wait': '0.1-0.8',
'input_wait': '0.3-0.8',
'submit_wait': '0.5-1.5',
'verification_code_input': '0.1-0.3', # 验证码输入间隔
'verification_success_wait': '2-3', # 验证成功后等待
'verification_retry_wait': '2-3', # 验证重试等待
'email_check_initial_wait': '4-6', # 首次等待邮件时间
'email_refresh_wait': '2-4', # 邮箱刷新等待时间
'settings_page_load_wait': '1-2', # 设置页面加载等待
'failed_retry_time': '0.5-1', # 验证失败重试等待时间
'retry_interval': '8-12', # 重试间隔时间
'max_timeout': '160' # 最大超时时间
}
}
# Add OS-specific path configurations
if sys.platform == "win32":
appdata = os.getenv("APPDATA")
default_config['WindowsPaths'] = {
'storage_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "storage.json"),
'sqlite_path': os.path.join(appdata, "Cursor", "User", "globalStorage", "state.vscdb"),
'machine_id_path': os.path.join(os.getenv("APPDATA"), "Cursor", "machineId"),
'cursor_path': os.path.join(os.getenv("LOCALAPPDATA", ""), "Programs", "Cursor", "resources", "app")
}
elif sys.platform == "darwin":
default_config['MacPaths'] = {
'storage_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.expanduser("~/Library/Application Support/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/Library/Application Support/Cursor/machineId"),
'cursor_path': "/Applications/Cursor.app/Contents/Resources/app"
}
elif sys.platform == "linux":
sudo_user = os.environ.get('SUDO_USER')
actual_home = f"/home/{sudo_user}" if sudo_user else os.path.expanduser("~")
default_config['LinuxPaths'] = {
'storage_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/storage.json")),
'sqlite_path': os.path.abspath(os.path.join(actual_home, ".config/Cursor/User/globalStorage/state.vscdb")),
'machine_id_path': os.path.expanduser("~/.config/Cursor/machineId"),
'cursor_path': "/opt/Cursor/resources/app" # 默認路徑
}
if os.path.exists(config_file):
config.read(config_file)
config_modified = False
# 检查并添加缺失的配置项
for section, options in default_config.items():
if not config.has_section(section):
config.add_section(section)
config_modified = True
for option, value in options.items():
if not config.has_option(section, option):
config.set(section, option, value)
config_modified = True
if translator:
print(f"{Fore.YELLOW} {translator.get('register.config_option_added', option=f'{section}.{option}') if translator else f'添加配置项: {section}.{option}'}{Style.RESET_ALL}")
# 如果有新增配置项,保存文件
if config_modified:
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_updated') if translator else '配置文件已更新'}{Style.RESET_ALL}")
else:
# 创建新配置文件
config = configparser.ConfigParser()
for section, options in default_config.items():
config.add_section(section)
for option, value in options.items():
config.set(section, option, value)
with open(config_file, 'w', encoding='utf-8') as f:
config.write(f)
if translator:
print(f"{Fore.GREEN}{translator.get('register.config_created') if translator else '已创建配置文件'}: {config_file}{Style.RESET_ALL}")
return config
except Exception as e:
if translator:
print(f"{Fore.RED}{translator.get('register.config_setup_error', error=str(e)) if translator else f'配置设置出错: {str(e)}'}{Style.RESET_ALL}")
raise
def setup_driver(translator=None):
"""Setup browser driver"""
try:
# 获取配置
config = setup_config(translator)
config = get_config(translator)
# Get Chrome path
chrome_path = config.get('Chrome', 'chromepath', fallback=get_default_chrome_path())
@ -449,57 +341,39 @@ def generate_password(length=12):
chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*"
return ''.join(random.choices(chars, k=length))
def fill_password(page, password: str, config, translator=None) -> bool:
def fill_password(page, password: str, config, translator=None):
"""
填写密码表单
"""
try:
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_password') if translator else '设置密码'}{Style.RESET_ALL}")
# 等待密码框出现并尝试多次
max_retries = 5
for i in range(max_retries):
# 检查是否出现错误信息
if page.ele("This email is not available."):
print(f"{Fore.RED}{translator.get('register.email_used') if translator else '注册失败:邮箱已被使用'}{Style.RESET_ALL}")
return False
# 查找密码输入框
# 填写密码
password_input = page.ele("@name=password")
print(f"{Fore.CYAN}🔑 {translator.get('register.setting_on_password')}: {password}{Style.RESET_ALL}")
if password_input:
# 清除可能存在的旧值并输入新密码
password_input.click()
time.sleep(get_random_wait_time(config, 'input_wait'))
password_input.input(password)
time.sleep(get_random_wait_time(config, 'input_wait'))
# 查找并点击提交按钮
# 点击提交按钮
submit_button = page.ele("@type=submit")
if submit_button:
submit_button.click()
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else '密码已提交'}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'submit_wait'))
print(f"{Fore.GREEN}{translator.get('register.password_submitted') if translator else '密码已提交'}{Style.RESET_ALL}")
return True
else:
print(f"{Fore.YELLOW}⚠️ {translator.get('register.retry_submit') if translator else '未找到提交按钮,重试中...'}{Style.RESET_ALL}")
# 如果没找到密码框,等待后重试
time.sleep(get_random_wait_time(config, 'failed_retry_time'))
if i < max_retries - 1: # 不是最后一次尝试时才打印
print(f"{Fore.YELLOW}⚠️ {translator.get('register.retry_password', attempt=i+1) if translator else f'{i+1} 次尝试设置密码...'}{Style.RESET_ALL}")
print(f"{Fore.RED}{translator.get('register.password_set_failed') if translator else '密码设置失败:超过重试次数'}{Style.RESET_ALL}")
return False
except Exception as e:
print(f"{Fore.RED}{translator.get('register.password_error', error=str(e)) if translator else f'设置密码时出错: {str(e)}'}{Style.RESET_ALL}")
return False
def handle_verification_code(browser_tab, email_tab, controller, email, password, config, translator=None):
def handle_verification_code(browser_tab, email_tab, controller, config, translator=None):
"""处理验证码"""
try:
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
# 检查是否使用手动输入验证码
if hasattr(controller, 'get_verification_code') and email_tab is None: # 手动模式
@ -520,7 +394,7 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
time.sleep(get_random_wait_time(config, 'verification_retry_wait'))
# 访问设置页面
print(f"{Fore.CYAN} {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
print(f"{Fore.CYAN}🔑 {translator.get('register.visiting_url')}: https://www.cursor.com/settings{Style.RESET_ALL}")
browser_tab.get("https://www.cursor.com/settings")
time.sleep(get_random_wait_time(config, 'settings_page_load_wait'))
return True, browser_tab
@ -529,7 +403,7 @@ def handle_verification_code(browser_tab, email_tab, controller, email, password
# 自动获取验证码逻辑
elif email_tab:
print(f"{translator.get('register.waiting_for_verification_code')}")
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'email_check_initial_wait'))
# 使用已有的 email_tab 刷新邮箱
@ -703,13 +577,11 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
# 访问注册页面
url = "https://authenticator.cursor.sh/sign-up"
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.visiting_url')}: {url}{Style.RESET_ALL}")
# 访问页面
simulate_human_input(page, url, config, translator)
if translator:
print(f"{Fore.CYAN}{translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}🔄 {translator.get('register.waiting_for_page_load')}{Style.RESET_ALL}")
time.sleep(get_random_wait_time(config, 'page_load_wait'))
# 如果没有提供账号信息,则生成随机信息
@ -729,34 +601,33 @@ def main(email=None, password=None, first_name=None, last_name=None, email_tab=N
# 填写表单
if fill_signup_form(page, first_name, last_name, email, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}")
print(f"\n{Fore.GREEN}{translator.get('register.form_submitted')}{Style.RESET_ALL}")
# 处理第一次 Turnstile 验证
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
print(f"\n{Fore.GREEN}{translator.get('register.first_verification_passed')}{Style.RESET_ALL}")
# 填写密码
if fill_password(page, password, config, translator):
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
time.sleep(2)
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_second_verification')}{Style.RESET_ALL}")
# 处理第二次 Turnstile 验证
if handle_turnstile(page, config, translator):
if translator:
print(f"\n{Fore.CYAN}{translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
if handle_verification_code(page, email_tab, controller, email, password, config, translator):
print(f"\n{Fore.CYAN}🔄 {translator.get('register.waiting_for_verification_code')}{Style.RESET_ALL}")
if handle_verification_code(page, email_tab, controller, config, translator):
success = True
return True, page
else:
print(f"\n{Fore.RED} {translator.get('register.verification_code_processing_failed') if translator else '验证码处理失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED} {translator.get('register.verification_code_processing_failed') if translator else '验证码处理失败'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED} {translator.get('register.second_verification_failed') if translator else '第二次验证失败'}{Style.RESET_ALL}")
else:
print(f"\n{Fore.RED} {translator.get('register.first_verification_failed') if translator else '第一次验证失败'}{Style.RESET_ALL}")
print(f"\n{Fore.RED} {translator.get('register.first_verification_failed') if translator else '第一次验证失败'}{Style.RESET_ALL}")
return False, None

View File

@ -13,6 +13,7 @@ from typing import Tuple
import configparser
from new_signup import get_user_documents_path
import traceback
from config import get_config
# Initialize colorama
init()
@ -39,7 +40,7 @@ def get_cursor_paths(translator=None) -> Tuple[str, str]:
if not os.path.exists(config_file):
raise OSError(translator.get('reset.config_not_found') if translator else "找不到配置文件")
config.read(config_file)
config.read(config_file, encoding='utf-8') # 指定編碼
# 根據系統獲取路徑
if system == "Darwin":
@ -408,7 +409,7 @@ class MachineIDResetter:
if not os.path.exists(config_file):
raise FileNotFoundError(f"Config file not found: {config_file}")
config.read(config_file)
config.read(config_file, encoding='utf-8')
# Check operating system
if sys.platform == "win32": # Windows
@ -690,7 +691,9 @@ class MachineIDResetter:
return False
def run(translator=None):
"""Convenient function for directly calling the reset function"""
config = get_config(translator)
if not config:
return False
print(f"\n{Fore.CYAN}{'='*50}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{EMOJI['RESET']} {translator.get('reset.title')}{Style.RESET_ALL}")
print(f"{Fore.CYAN}{'='*50}{Style.RESET_ALL}")

32
utils.py Normal file
View File

@ -0,0 +1,32 @@
import os
import sys
import platform
def get_user_documents_path():
"""Get user documents path"""
if platform.system() == "Windows":
return os.path.expanduser("~\\Documents")
else:
return os.path.expanduser("~/Documents")
def get_default_chrome_path():
"""Get default Chrome path"""
if sys.platform == "win32":
return r"C:\Program Files\Google\Chrome\Application\chrome.exe"
elif sys.platform == "darwin":
return "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
else:
return "/usr/bin/google-chrome"
def get_linux_cursor_path():
"""Get Linux Cursor path"""
possible_paths = [
"/opt/Cursor/resources/app",
"/usr/share/cursor/resources/app",
"/opt/cursor-bin/resources/app",
"/usr/lib/cursor/resources/app",
os.path.expanduser("~/.local/share/cursor/resources/app")
]
# 返回第一个存在的路径,如果都不存在则返回第一个路径
return next((path for path in possible_paths if os.path.exists(path)), possible_paths[0])