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