diff --git a/.docker/README.md b/.docker/README.md new file mode 100644 index 0000000..139b396 --- /dev/null +++ b/.docker/README.md @@ -0,0 +1,41 @@ +# Docker + +The docker environment has been put together by [0x78f1935](https://github.com/0x78f1935), just like this readme file. + +## Overview + +Start the environment with `docker compose up --build -d`. Once the service is running the following ports are available by default: + +| Port | Internal Port | What is it? | +| ---- | ------------- | ---------------- | +| 8088 | 8088 | Turnstile Solver | +| 5901 | 5901 | VNC Server | + +### Turnstile Solver + +Simply make your Python / Curl requests to this server as documented in the main readme. + +### VNC Server + +You can connect without a password to the VNC server, which will show you how the browser is being handled. In addition it allows you to manually manipulate the page if neceserry. Very nice for debugging. + +It also shows you that we run none-headless. + +## Internal connection + +To talk with the internal port, connect your docker environment to the `solver` network. When doing so, the dns name `captcha_resolver` should become available to you and resolves the turnstile docker IP addr. + +- Connect to `solver` network + +```sh +docker network connect solver CONTAINER +``` + +Where `CONTAINER` is your container. + +## Recommendation + +I highly recommend in production to: + +- Disable VNC port. Only use this for development purposes +- Disable Turnstile remote port, only use the internal docker network diff --git a/.docker/services/turnstile/entrypoint.sh b/.docker/services/turnstile/entrypoint.sh new file mode 100644 index 0000000..8e78b84 --- /dev/null +++ b/.docker/services/turnstile/entrypoint.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Start Xvfb +Xvfb :99 -screen 0 1024x768x16 & +XVFB_PID=$! + +# Wait for Xvfb to become ready +for i in {1..10}; do + if xdpyinfo -display :99 >/dev/null 2>&1; then + echo "Xvfb is ready!" + break + fi + echo "Waiting for Xvfb to start..." + sleep 1 +done + +# If it's not ready after 10s, bail +if ! xdpyinfo -display :99 >/dev/null 2>&1; then + echo "Xvfb did not start!" + exit 1 +fi + +# Start window manager +fluxbox & + +# Start VNC server +x11vnc -display :99 -forever -nopw -shared -listen 0.0.0.0 -rfbport 5901 -bg + +# Run your app +export DISPLAY=:99 +exec python main.py --browser-args "--no-sandbox --disable-gpu --disable-software-rasterizer --disable-dev-shm-usage --disable-setuid-sandbox --start-maximized --disable-blink-features=AutomationControlled --lang=en-US,en --window-size=1024,720 --enable-unsafe-webgpu --enable-webgl --use-gl=swiftshader --enable-features=SharedArrayBuffer,TrustTokens,PrivateNetworkAccessChecksBypassingPermissionPolicy" diff --git a/.docker/services/turnstile/solver.Dockerfile b/.docker/services/turnstile/solver.Dockerfile new file mode 100644 index 0000000..2ded5b5 --- /dev/null +++ b/.docker/services/turnstile/solver.Dockerfile @@ -0,0 +1,32 @@ +FROM python:3.13.3 + +# Prevent interactive prompts during package installs +ENV DEBIAN_FRONTEND=noninteractive +ENV DISPLAY=:99 + +# Install xvfb and VNC server +RUN apt-get update && apt-get install -y \ + xvfb \ + fluxbox \ + x11vnc \ + supervisor \ + x11-utils \ + --no-install-recommends \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +# Install requirements +RUN pip install patchright && \ + patchright install chromium --with-deps + +WORKDIR /workspaceFolder +COPY . . +RUN pip install -r requirements.txt +RUN pip install . + +WORKDIR /workspaceFolder/src/turnstile_solver + +HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ + CMD xvfb-run --auto-servernum echo "Xvfb is working!" || exit 1 + +RUN chmod +x /workspaceFolder/.docker/services/turnstile/entrypoint.sh +ENTRYPOINT [ "/workspaceFolder/.docker/services/turnstile/entrypoint.sh" ] diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..856f7d2 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,25 @@ +{ + "configurations": [ + { + "name": "Turnstile: Headless", + "type": "debugpy", + "request": "launch", + "module": "turnstile_solver", + "args": [ + "--headless", + "--port", + "8088" + ] + }, + { + "name": "Turnstile: None-Headless", + "type": "debugpy", + "request": "launch", + "module": "turnstile_solver", + "args": [ + "--port", + "8088" + ] + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 6641cf4..ab8b484 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,23 @@ Python server to automatically solve Cloudflare Turnstile CAPTCHA with an averag PD: This repository was initially created for personal use. I've adjusted it for sharing, but it might still be slightly disorganized. Feel free to contribute, open issues, and request new features. +## Table of Content + +- [Screenshots](#screenshots) +- [Install](#install) + - [Patchright](#install-patchright-patched-chromium-browser) +- [How to use](#how-to-use) + - [Run server](#run-server) + - [Global proxy](#use-global-browser-proxy) + - [Proxy parameters](#load-proxy-parameters-from-environment-variables-all-caps) + - [Proxy file](#use-a-proxy-from-file-per-browser-context) +- [Get Token](#get-token) + - [Curl](#curl) + - [Python](#python) +- [Docker Micro Service](./.docker/README.md) +- [Disclaimer](#disclaimer-️) +- [Donate](#donate) + ## Screenshots TODO: Update @@ -14,10 +31,13 @@ TODO: Update ![Browser](images/browser.png) ## Install + ```bash pip install git+https://github.com/odell0111/turnstile_solver@main ``` + ### Install Patchright patched chromium browser + ```bash patchright install chromium ``` @@ -25,6 +45,7 @@ patchright install chromium ## How to use ### Run server + ```bash solver ``` @@ -32,23 +53,29 @@ solver ```bash solver --port 8088 --secret jWRN7DH6 --browser-position --max-attempts 3 --captcha-timeout 30 --page-load-timeout 30 --reload-on-overrun ``` + #### Use global browser proxy + ```bash solver --proxy-server http://myproxy.com:3128 --proxy-username user --proxy-password pass ``` + ##### Load proxy parameters from environment variables (all caps) + ```bash solver --proxy-server MY_PROXY_SERVER --proxy-username MY_PROXY_USERNAME --proxy-password MY_PROXY_PASSWORD ``` + ##### Use a proxy from file per browser context + ```bash solver --proxies myproxies.txt ``` - ### Get token #### cURL + ```bash curl --location --request GET 'http://127.0.0.1:8088/solve' \ --header 'ngrok-skip-browser-warning: _' \ @@ -61,6 +88,7 @@ curl --location --request GET 'http://127.0.0.1:8088/solve' \ ``` #### Python + ```python import requests @@ -80,8 +108,8 @@ json_data = { } response = requests.get( - url=url, - headers=headers, + url=url, + headers=headers, json=json_data, ) @@ -100,31 +128,39 @@ print("Token:", token) ``` -## Disclaimer ‼️ +## Disclaimer ‼️ + Use this project entirely at your own risk. I hold no responsibility for any negative outcomes, including but not limited to API blocking and IP bans ## Donate -If you find my work useful and want to encourage further development, you can do so by donating -[//]: # ([![Donate](https://app.oxapay.com/media/btn/light-btn.png)](https://oxapay.com/donate/42319117)) +If you find my work useful and want to encourage further development, you can do so by donating -[//]: # ( ) +[//]: # '[![Donate](https://app.oxapay.com/media/btn/light-btn.png)](https://oxapay.com/donate/42319117)' +[//]: # ' ' ### [OxaPay](https://oxapay.com/donate/42319117) ### TON + ``` UQCyCnWVYOmv97idVFZ4tIewToZacRhYVwfGNU658fN5w3Kl ``` + ### Bitcoin + ``` 1E9kw3FuaahfeproboNL7uvyBdjP9wY6CR ``` + ### Bitcoin (BEP20) + ``` 0x88046e6d0f2bf8629cd7fbd754e4e275083fc993 ``` + #### Speed Lightning Address username + ``` bytechanger@speed.app -``` \ No newline at end of file +``` diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..e97d928 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,20 @@ +services: + solver: + container_name: solver + image: turnstile_solver + hostname: solver + build: + context: . + dockerfile: ./.docker/services/turnstile/solver.Dockerfile + restart: always + ports: + # Recommendation: Disable both ports in production + - 8088:8088 + - 5901:5901 + networks: + - solver + +networks: + solver: + name: solver + driver: bridge diff --git a/src/turnstile_solver/__init__.py b/src/turnstile_solver/__init__.py index 23bb1e5..555ffb2 100644 --- a/src/turnstile_solver/__init__.py +++ b/src/turnstile_solver/__init__.py @@ -1,4 +1,5 @@ -from .solver import TurnstileSolver -from .turnstile_solver_server import TurnstileSolverServer -from .constants import HOST, PORT -from .main import main, run_server, main_cli +# -*- mode: python ; coding: utf-8 -*- +from turnstile_solver.solver import TurnstileSolver +from turnstile_solver.turnstile_solver_server import TurnstileSolverServer +from turnstile_solver.constants import HOST, PORT +from turnstile_solver.main import main, run_server, main_cli diff --git a/src/turnstile_solver/__main__.py b/src/turnstile_solver/__main__.py index 79ae050..296e4c2 100644 --- a/src/turnstile_solver/__main__.py +++ b/src/turnstile_solver/__main__.py @@ -1,6 +1,7 @@ +# -*- mode: python ; coding: utf-8 -*- import asyncio -from .main import main +from turnstile_solver.main import main if __name__ == '__main__': asyncio.run(main()) diff --git a/src/turnstile_solver/browser_context_pool.py b/src/turnstile_solver/browser_context_pool.py index abeb649..6a52637 100644 --- a/src/turnstile_solver/browser_context_pool.py +++ b/src/turnstile_solver/browser_context_pool.py @@ -1,16 +1,17 @@ +# -*- mode: python ; coding: utf-8 -*- import asyncio import logging from typing import TYPE_CHECKING from patchright.async_api import Browser -from .constants import MAX_PAGES_PER_CONTEXT, MAX_CONTEXTS -from .page_pool import PagePool -from .pool import Pool -from .proxy_provider import ProxyProvider +from turnstile_solver.constants import MAX_PAGES_PER_CONTEXT, MAX_CONTEXTS +from turnstile_solver.page_pool import PagePool +from turnstile_solver.pool import Pool +from turnstile_solver.proxy_provider import ProxyProvider if TYPE_CHECKING: - from .solver import TurnstileSolver + from turnstile_solver.solver import TurnstileSolver logger = logging.getLogger(__name__) diff --git a/src/turnstile_solver/constants.py b/src/turnstile_solver/constants.py index 617a941..ceadbb9 100644 --- a/src/turnstile_solver/constants.py +++ b/src/turnstile_solver/constants.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- import os from pathlib import Path diff --git a/src/turnstile_solver/custom_rich_help_formatter.py b/src/turnstile_solver/custom_rich_help_formatter.py index 9f9527b..601682d 100644 --- a/src/turnstile_solver/custom_rich_help_formatter.py +++ b/src/turnstile_solver/custom_rich_help_formatter.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- from rich.console import RenderableType from rich_argparse import RichHelpFormatter diff --git a/src/turnstile_solver/enums.py b/src/turnstile_solver/enums.py index 1cf1186..6799104 100644 --- a/src/turnstile_solver/enums.py +++ b/src/turnstile_solver/enums.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- from enum import Enum diff --git a/src/turnstile_solver/main.py b/src/turnstile_solver/main.py index 9f4cd82..cb4accd 100644 --- a/src/turnstile_solver/main.py +++ b/src/turnstile_solver/main.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- import asyncio import logging @@ -18,15 +19,15 @@ from rich.text import Text from multiprocessing import Process -from . import constants as c -from .proxy import Proxy -from .proxy_provider import ProxyProvider -from .custom_rich_help_formatter import CustomRichHelpFormatter -from .solver_console import SolverConsole -from .solver_console_highlighter import SolverConsoleHighlighter -from .solver import TurnstileSolver -from .utils import init_logger, simulate_intensive_task, get_file_handler, load_proxy_param -from .turnstile_solver_server import TurnstileSolverServer +from turnstile_solver import constants as c +from turnstile_solver.proxy import Proxy +from turnstile_solver.proxy_provider import ProxyProvider +from turnstile_solver.custom_rich_help_formatter import CustomRichHelpFormatter +from turnstile_solver.solver_console import SolverConsole +from turnstile_solver.solver_console_highlighter import SolverConsoleHighlighter +from turnstile_solver.solver import TurnstileSolver +from turnstile_solver.utils import init_logger, simulate_intensive_task, get_file_handler, load_proxy_param +from turnstile_solver.turnstile_solver_server import TurnstileSolverServer _console = SolverConsole() diff --git a/src/turnstile_solver/page_pool.py b/src/turnstile_solver/page_pool.py index 9b53ca1..51cfd52 100644 --- a/src/turnstile_solver/page_pool.py +++ b/src/turnstile_solver/page_pool.py @@ -1,9 +1,10 @@ +# -*- mode: python ; coding: utf-8 -*- import logging from patchright.async_api import BrowserContext, Page, Route from turnstile_solver.pool import Pool -from .constants import MAX_PAGES_PER_CONTEXT +from turnstile_solver.constants import MAX_PAGES_PER_CONTEXT logger = logging.getLogger(__name__) diff --git a/src/turnstile_solver/pool.py b/src/turnstile_solver/pool.py index d14ec64..8b997c4 100644 --- a/src/turnstile_solver/pool.py +++ b/src/turnstile_solver/pool.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- import asyncio import logging from collections import deque diff --git a/src/turnstile_solver/proxy.py b/src/turnstile_solver/proxy.py index 9bf9f22..9e56217 100644 --- a/src/turnstile_solver/proxy.py +++ b/src/turnstile_solver/proxy.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- import json import logging import re diff --git a/src/turnstile_solver/proxy_provider.py b/src/turnstile_solver/proxy_provider.py index 69f9c9e..10f398e 100644 --- a/src/turnstile_solver/proxy_provider.py +++ b/src/turnstile_solver/proxy_provider.py @@ -1,7 +1,8 @@ +# -*- mode: python ; coding: utf-8 -*- import logging from pathlib import Path -from .proxy import Proxy +from turnstile_solver.proxy import Proxy logger = logging.getLogger(__name__) diff --git a/src/turnstile_solver/solver.py b/src/turnstile_solver/solver.py index f72d6b8..bc7d7cb 100644 --- a/src/turnstile_solver/solver.py +++ b/src/turnstile_solver/solver.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- import datetime import logging import time @@ -5,19 +6,16 @@ from typing import Callable, Awaitable from patchright.async_api import async_playwright, Page, BrowserContext, Browser, Playwright -from . import constants as c -from .enums import CaptchaApiMessageEvent -from .proxy import Proxy -from .solver_console import SolverConsole -from .turnstile_result import TurnstileResult -from .turnstile_solver_server import TurnstileSolverServer, CAPTCHA_EVENT_CALLBACK_ENDPOINT +from turnstile_solver import constants as c +from turnstile_solver.enums import CaptchaApiMessageEvent +from turnstile_solver.proxy import Proxy +from turnstile_solver.solver_console import SolverConsole +from turnstile_solver.turnstile_result import TurnstileResult +from turnstile_solver.turnstile_solver_server import TurnstileSolverServer, CAPTCHA_EVENT_CALLBACK_ENDPOINT logger = logging.getLogger(__name__) BROWSER_ARGS = { - "--disable-blink-features=AutomationControlled", # avoid navigator.webdriver detection - "--no-sandbox", - "--disable-dev-shm-usage", "--disable-background-networking", "--disable-background-timer-throttling", "--disable-backgrounding-occluded-windows", @@ -63,6 +61,21 @@ '--log-level=3', '--proxy-bypass-list=<-loopback>;localhost;127.0.0.1;*.local', + # Run the browser with SwiftShader and force it to accept software rendering + '--no-sandbox', + '--disable-gpu', + '--disable-software-rasterizer', + '--disable-dev-shm-usage', + '--disable-blink-features=AutomationControlled', # avoid navigator.webdriver detection + '--disable-setuid-sandbox', + '--lang=en-US,en', + '--start-maximized', + '--window-size=1024,720', + '--enable-unsafe-webgpu', + '--enable-webgl', + '--use-gl=swiftshader', + '--enable-features=SharedArrayBuffer,TrustTokens,PrivateNetworkAccessChecksBypassingPermissionPolicy', + # Not needed, here just for reference # Network/Connection Tuning # '--enable-features=NetworkService,ParallelDownloading', diff --git a/src/turnstile_solver/solver_console.py b/src/turnstile_solver/solver_console.py index a1766c4..d6c6b6a 100644 --- a/src/turnstile_solver/solver_console.py +++ b/src/turnstile_solver/solver_console.py @@ -1,8 +1,9 @@ +# -*- mode: python ; coding: utf-8 -*- from rich.console import Console from rich.theme import Theme -from .constants import CONSOLE_THEME_STYLES -from .solver_console_highlighter import SolverConsoleHighlighter +from turnstile_solver.constants import CONSOLE_THEME_STYLES +from turnstile_solver.solver_console_highlighter import SolverConsoleHighlighter MAX_CONSOLE_WIDTH = 200 diff --git a/src/turnstile_solver/solver_console_highlighter.py b/src/turnstile_solver/solver_console_highlighter.py index 75bd591..3cc2e25 100644 --- a/src/turnstile_solver/solver_console_highlighter.py +++ b/src/turnstile_solver/solver_console_highlighter.py @@ -1,3 +1,4 @@ +# -*- mode: python ; coding: utf-8 -*- from rich.highlighter import ReprHighlighter _ORIGINAL_NUMBER_HIGHLIGHTER = r"(?P(?