diff --git a/Dockerfile.backend b/Dockerfile.backend index f6dec0f..11d5bbd 100644 --- a/Dockerfile.backend +++ b/Dockerfile.backend @@ -10,7 +10,7 @@ RUN apt-get update -y && apt-get install -y \ unzip \ xvfb \ libxss1 \ - libappindicator1 \ + #libappindicator1 \ fonts-liberation \ libnss3 \ libatk1.0-0 \ diff --git a/docker-compose.yml b/docker-compose.yml index d3927b7..47748d5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3' +version: '3.8' services: redis: @@ -77,9 +77,6 @@ services: dockerfile: Dockerfile.backend ports: - ${BACKEND_PORT:-7777}:${BACKEND_PORT:-7777} - - ${OLLAMA_PORT:-11434}:${OLLAMA_PORT:-11434} - - ${LM_STUDIO_PORT:-1234}:${LM_STUDIO_PORT:-1234} - - ${CUSTOM_ADDITIONAL_LLM_PORT:-8000}:${CUSTOM_ADDITIONAL_LLM_PORT:-8000} volumes: - ./:/app - ${WORK_DIR:-.}:/opt/workspace diff --git a/sources/browser.py b/sources/browser.py index 5cb52f8..92d7244 100644 --- a/sources/browser.py +++ b/sources/browser.py @@ -121,29 +121,8 @@ def bypass_ssl() -> str: pretty_print("Bypassing SSL verification issues, we strongly advice you update your certifi SSL certificate.", color="warning") ssl._create_default_https_context = ssl._create_unverified_context -def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome: - """Create an undetected ChromeDriver instance.""" - try: - driver = uc.Chrome(service=service, options=chrome_options) - except Exception as e: - pretty_print(f"Failed to create Chrome driver: {str(e)}. Trying to bypass SSL...", color="failure") - try: - bypass_ssl() - driver = uc.Chrome(service=service, options=chrome_options) - except Exception as e: - pretty_print(f"Failed to create Chrome driver, fallback failed:\n{str(e)}.", color="failure") - raise e - raise e - driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") - return driver - -def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> webdriver.Chrome: - """Create a Chrome WebDriver with specified options.""" - # Warn if trying to run non-headless in Docker - if not headless and os.path.exists('/.dockerenv'): - print("[WARNING] Running non-headless browser in Docker may fail!") - print("[WARNING] Consider setting headless=True or headless_browser=True in config.ini") - +def create_chrome_options(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> Options: + """Create Chrome options - separated for reusability.""" chrome_options = Options() chrome_path = get_chrome_path() @@ -152,17 +131,17 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx chrome_options.binary_location = chrome_path if headless: - #chrome_options.add_argument("--headless") chrome_options.add_argument("--headless=new") chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--disable-webgl") - user_data_dir = tempfile.mkdtemp() + user_agent = get_random_user_agent() width, height = (1920, 1080) - user_data_dir = tempfile.mkdtemp(prefix="chrome_profile_") + profile_dir = f"/tmp/chrome_profile_{uuid.uuid4().hex[:8]}" + + # Core options chrome_options.add_argument("--no-sandbox") chrome_options.add_argument('--disable-dev-shm-usage') - profile_dir = f"/tmp/chrome_profile_{uuid.uuid4().hex[:8]}" chrome_options.add_argument(f'--user-data-dir={profile_dir}') chrome_options.add_argument(f"--accept-lang={lang}-{lang.upper()},{lang};q=0.9") chrome_options.add_argument("--disable-extensions") @@ -182,19 +161,76 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx chrome_options.add_argument("--disable-blink-features=AutomationControlled") chrome_options.add_argument(f'user-agent={user_agent["ua"]}') chrome_options.add_argument(f'--window-size={width},{height}') + if not stealth_mode: if not os.path.exists(crx_path): pretty_print(f"Anti-captcha CRX not found at {crx_path}.", color="failure") else: chrome_options.add_extension(crx_path) + + if not stealth_mode: + security_prefs = { + "profile.default_content_setting_values.geolocation": 0, + "profile.default_content_setting_values.notifications": 0, + "profile.default_content_setting_values.camera": 0, + "profile.default_content_setting_values.microphone": 0, + "profile.default_content_setting_values.midi_sysex": 0, + "profile.default_content_setting_values.clipboard": 0, + "profile.default_content_setting_values.media_stream": 0, + "profile.default_content_setting_values.background_sync": 0, + "profile.default_content_setting_values.sensors": 0, + "profile.default_content_setting_values.accessibility_events": 0, + "safebrowsing.enabled": True, + "credentials_enable_service": False, + "profile.password_manager_enabled": False, + "webkit.webprefs.accelerated_2d_canvas_enabled": True, + "webkit.webprefs.force_dark_mode_enabled": False, + "webkit.webprefs.accelerated_2d_canvas_msaa_sample_count": 4, + "enable_webgl": True, + "enable_webgl2_compute_context": True + } + chrome_options.add_experimental_option("prefs", security_prefs) + chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) + chrome_options.add_experimental_option('useAutomationExtension', False) + + return chrome_options +def create_undetected_chromedriver(service, chrome_options) -> webdriver.Chrome: + """Create an undetected ChromeDriver instance with proper error handling.""" + try: + driver = uc.Chrome(service=service, options=chrome_options) + except Exception as e: + pretty_print(f"Failed to create Chrome driver: {str(e)}. Trying to bypass SSL...", color="failure") + try: + bypass_ssl() + # Create NEW options object - this is the key fix + fresh_options = create_chrome_options( + headless=any("--headless" in arg for arg in chrome_options.arguments), + stealth_mode=True, # We're in stealth mode if we reach this point + crx_path="./crx/nopecha.crx" # Default path + ) + driver = uc.Chrome(service=service, options=fresh_options) + except Exception as e: + pretty_print(f"Failed to create Chrome driver, fallback failed:\n{str(e)}.", color="failure") + raise e + + driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") + return driver + +def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx", lang="en") -> webdriver.Chrome: + """Create a Chrome WebDriver with specified options.""" + # Warn if trying to run non-headless in Docker + if not headless and os.path.exists('/.dockerenv'): + print("[WARNING] Running non-headless browser in Docker may fail!") + print("[WARNING] Consider setting headless=True or headless_browser=True in config.ini") + + chrome_options = create_chrome_options(headless, stealth_mode, crx_path, lang) chromedriver_path = install_chromedriver() - service = Service(chromedriver_path) + if stealth_mode: - chrome_options.add_argument("--disable-blink-features=AutomationControlled") driver = create_undetected_chromedriver(service, chrome_options) - chrome_version = driver.capabilities['browserVersion'] + user_agent = get_random_user_agent() stealth(driver, languages=["en-US", "en"], vendor=user_agent["vendor"], @@ -204,30 +240,8 @@ def create_driver(headless=False, stealth_mode=True, crx_path="./crx/nopecha.crx fix_hairline=True, ) return driver - security_prefs = { - "profile.default_content_setting_values.geolocation": 0, - "profile.default_content_setting_values.notifications": 0, - "profile.default_content_setting_values.camera": 0, - "profile.default_content_setting_values.microphone": 0, - "profile.default_content_setting_values.midi_sysex": 0, - "profile.default_content_setting_values.clipboard": 0, - "profile.default_content_setting_values.media_stream": 0, - "profile.default_content_setting_values.background_sync": 0, - "profile.default_content_setting_values.sensors": 0, - "profile.default_content_setting_values.accessibility_events": 0, - "safebrowsing.enabled": True, - "credentials_enable_service": False, - "profile.password_manager_enabled": False, - "webkit.webprefs.accelerated_2d_canvas_enabled": True, - "webkit.webprefs.force_dark_mode_enabled": False, - "webkit.webprefs.accelerated_2d_canvas_msaa_sample_count": 4, - "enable_webgl": True, - "enable_webgl2_compute_context": True - } - chrome_options.add_experimental_option("prefs", security_prefs) - chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) - chrome_options.add_experimental_option('useAutomationExtension', False) - return webdriver.Chrome(service=service, options=chrome_options) + else: + return webdriver.Chrome(service=service, options=chrome_options) class Browser: def __init__(self, driver, anticaptcha_manual_install=False): diff --git a/start_services.sh b/start_services.sh index 967a5b7..af9f354 100755 --- a/start_services.sh +++ b/start_services.sh @@ -64,19 +64,18 @@ else fi # Check if Docker Compose is installed -if ! command_exists docker-compose && ! docker compose version >/dev/null 2>&1; then - echo "Error: Docker Compose is not installed. Please install it first." - echo "On Ubuntu: sudo apt install docker-compose" - echo "Or via pip: pip install docker-compose" - exit 1 -fi - -if command_exists "docker compose"; then - echo "Using newer docker compose (not docker-compose)." +# Prefer the newer 'docker compose' command if available +if docker compose version >/dev/null 2>&1; then + echo "Using newer docker compose (v2)." COMPOSE_CMD="docker compose" -else +elif command_exists docker-compose; then echo "Using old docker-compose." COMPOSE_CMD="docker-compose" +else + echo "Error: Docker Compose is not installed. Please install it first." + echo "On Ubuntu: sudo apt install docker-compose-plugin" + echo "Or install Docker Desktop which includes compose v2" + exit 1 fi # Check if docker-compose.yml exists