├── .gitignore ├── README.md ├── assets ├── ant.png ├── header.gif └── twocap.png ├── endpoints ├── __init__.py ├── ant.py └── twocap.py ├── main.py ├── poetry.lock ├── pyproject.toml ├── requirements.txt └── src ├── __init__.py ├── captcha_solver.py ├── http_client.py └── rich_console.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | *.py,cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | cover/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | .pybuilder/ 76 | target/ 77 | 78 | # Jupyter Notebook 79 | .ipynb_checkpoints 80 | 81 | # IPython 82 | profile_default/ 83 | ipython_config.py 84 | 85 | # pyenv 86 | # For a library or package, you might want to ignore these files since the code is 87 | # intended to run in multiple environments; otherwise, check them in: 88 | # .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # poetry 98 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 99 | # This is especially recommended for binary packages to ensure reproducibility, and is more 100 | # commonly ignored for libraries. 101 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 102 | #poetry.lock 103 | 104 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 105 | __pypackages__/ 106 | 107 | # Celery stuff 108 | celerybeat-schedule 109 | celerybeat.pid 110 | 111 | # SageMath parsed files 112 | *.sage.py 113 | 114 | # Environments 115 | .env 116 | .venv 117 | env/ 118 | venv/ 119 | ENV/ 120 | env.bak/ 121 | venv.bak/ 122 | 123 | # Spyder project settings 124 | .spyderproject 125 | .spyproject 126 | 127 | # Rope project settings 128 | .ropeproject 129 | 130 | # mkdocs documentation 131 | /site 132 | 133 | # mypy 134 | .mypy_cache/ 135 | .dmypy.json 136 | dmypy.json 137 | 138 | # Pyre type checker 139 | .pyre/ 140 | 141 | # pytype static type analyzer 142 | .pytype/ 143 | 144 | # Cython debug symbols 145 | cython_debug/ 146 | 147 | # PyCharm 148 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 149 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 150 | # and can be added to the global gitignore or merged into this file. For a more nuclear 151 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 152 | #.idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | **Recaptcha-V3** is a bypasser Google Recaptcha V3 with URL. 6 | 7 |
8 | 9 | ## **Installation** 10 | 11 | **Using** `poetry` 12 | 13 | ``` 14 | git clone https://github.com/x404xx/Recaptcha-V3.git 15 | cd Recaptcha-V3 16 | poetry shell 17 | poetry install 18 | ``` 19 | 20 | **Using** `pip` 21 | 22 | ``` 23 | git clone https://github.com/x404xx/Recaptcha-V3.git 24 | cd Recaptcha-V3 25 | virtualenv env 26 | env/scripts/activate 27 | pip install -r requirements.txt 28 | ``` 29 | 30 | ## Url 31 | 32 | ``` 33 | https://antcpt.com/score_detector/ 34 | https://2captcha.com/demo/recaptcha-v3-enterprise 35 | ``` 36 | 37 | ## Usage 38 | 39 | ``` 40 | python main.py 41 | ``` 42 | 43 | ## Output 44 | 45 |
46 | 47 | **antcpt.com** 48 | 49 | 50 | 51 | **2captcha.com** 52 | 53 | 54 | 55 |
56 | 57 | ## Todo 58 | 59 | - [x] Correct get api type 60 | 61 | > Sometimes the API type is not shown in the HTML. We need to implement it correctly to retrieve the API type. 62 | 63 | ## **Legal Disclaimer** 64 | 65 | > This was made for educational purposes only, nobody which directly involved in this project is responsible for any damages caused. **_You are responsible for your actions._** 66 | -------------------------------------------------------------------------------- /assets/ant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x404xx/Recaptcha-V3/db1b79dbb4bf0cde88476c1e2f0412462259f737/assets/ant.png -------------------------------------------------------------------------------- /assets/header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x404xx/Recaptcha-V3/db1b79dbb4bf0cde88476c1e2f0412462259f737/assets/header.gif -------------------------------------------------------------------------------- /assets/twocap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/x404xx/Recaptcha-V3/db1b79dbb4bf0cde88476c1e2f0412462259f737/assets/twocap.png -------------------------------------------------------------------------------- /endpoints/__init__.py: -------------------------------------------------------------------------------- 1 | from .ant import ant_endpoint 2 | from .twocap import twocap_endpoint 3 | -------------------------------------------------------------------------------- /endpoints/ant.py: -------------------------------------------------------------------------------- 1 | def ant_endpoint(client, captcha_token, page_action, user_agent, ip_address): 2 | """ 3 | Send a POST request to an endpoint with a captcha token and additional information. 4 | 5 | Args: 6 | client: The HTTP client used to make the POST request. 7 | captcha_token (str): The captcha token to be sent in the request. 8 | page_action (str): The action related to the page. 9 | user_agent (str): The user agent string. 10 | ip_address (str): The IP address of the client. 11 | 12 | Returns: 13 | dict: A dictionary containing the response JSON merged with additional information. 14 | """ 15 | response = client.post( 16 | "https://antcpt.com/score_detector/verify.php", 17 | headers={"Content-Type": "application/json"}, 18 | json={"g-recaptcha-response": captcha_token}, 19 | ) 20 | response_json = response.json() 21 | response_json.update( 22 | {"action": page_action, "user_agent": user_agent, "ip_address": ip_address} 23 | ) 24 | return response_json 25 | -------------------------------------------------------------------------------- /endpoints/twocap.py: -------------------------------------------------------------------------------- 1 | def twocap_endpoint( 2 | client, sitekey, captcha_token, page_action, user_agent, ip_address 3 | ): 4 | """ 5 | Send a POST request to the 2captcha endpoint with the provided data and return the updated JSON response. 6 | 7 | Args: 8 | client: The HTTP client used to make the POST request. 9 | sitekey: The site key for the captcha. 10 | captcha_token: The token generated for the captcha. 11 | page_action: The action associated with the page. 12 | user_agent: The user agent string. 13 | ip_address: The IP address used for the request. 14 | 15 | Returns: 16 | dict: The updated JSON response including additional data like action, user_agent, and ip_address. 17 | """ 18 | 19 | response = client.post( 20 | "https://2captcha.com/api/v1/captcha-demo/recaptcha-enterprise/verify", 21 | headers={"Content-Type": "application/json"}, 22 | json={ 23 | "siteKey": sitekey, 24 | "token": captcha_token, 25 | }, 26 | ) 27 | response_json = response.json() 28 | response_json.update( 29 | {"action": page_action, "user_agent": user_agent, "ip_address": ip_address} 30 | ) 31 | return response_json 32 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from endpoints import ant_endpoint, twocap_endpoint 2 | from src import CaptchaSolver, HttpClient, RichConsole 3 | 4 | # * PROXY URL examples 5 | # "http://username:password@host:port" 6 | # "socks5://username:password@host:port" 7 | 8 | VERBOSE = True 9 | LOG_HANDLER = True 10 | PROXY_URL = None 11 | 12 | ANT_URL = "https://antcpt.com/score_detector/" 13 | TWO_URL = "https://2captcha.com/demo/recaptcha-v3-enterprise" 14 | 15 | 16 | def get_ip(client): 17 | """ 18 | Retrieves the public IP address using the provided HTTP client. 19 | 20 | Args: 21 | client: The HTTP client used to make the request. 22 | 23 | Returns: 24 | str: The public IP address extracted from the JSON response. 25 | """ 26 | response = client.get("https://jsonip.com/") 27 | return response.json()["ip"] 28 | 29 | 30 | # TODO: Implement this section below based on the website that needs to be bypassed. 31 | def solve_v3(is_ant=False, is_two=False): 32 | BASE_URL = ANT_URL if is_ant else TWO_URL if is_two else None 33 | 34 | if BASE_URL is None: 35 | raise ValueError("Either 'is_ant' or 'is_two' must be True") 36 | 37 | solver = CaptchaSolver(BASE_URL, VERBOSE) 38 | 39 | with HttpClient(PROXY_URL, LOG_HANDLER) as client: 40 | ip_address = get_ip(client) 41 | user_agent = client.base_agent["User-Agent"] 42 | captcha_token = solver.solve_captcha(client) 43 | page_action = solver.page_action 44 | 45 | if is_two: 46 | sitekey = solver.sitekey 47 | return twocap_endpoint( 48 | client, sitekey, captcha_token, page_action, user_agent, ip_address 49 | ) 50 | elif is_ant: 51 | return ant_endpoint( 52 | client, captcha_token, page_action, user_agent, ip_address 53 | ) 54 | 55 | 56 | if __name__ == "__main__": 57 | RichConsole.clear() 58 | RichConsole.print(solve_v3(is_ant=True)) 59 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "anyio" 5 | version = "4.3.0" 6 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 7 | optional = false 8 | python-versions = ">=3.8" 9 | files = [ 10 | {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, 11 | {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, 12 | ] 13 | 14 | [package.dependencies] 15 | idna = ">=2.8" 16 | sniffio = ">=1.1" 17 | 18 | [package.extras] 19 | doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] 20 | test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] 21 | trio = ["trio (>=0.23)"] 22 | 23 | [[package]] 24 | name = "certifi" 25 | version = "2024.2.2" 26 | description = "Python package for providing Mozilla's CA Bundle." 27 | optional = false 28 | python-versions = ">=3.6" 29 | files = [ 30 | {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, 31 | {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, 32 | ] 33 | 34 | [[package]] 35 | name = "h11" 36 | version = "0.14.0" 37 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 38 | optional = false 39 | python-versions = ">=3.7" 40 | files = [ 41 | {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, 42 | {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, 43 | ] 44 | 45 | [[package]] 46 | name = "httpcore" 47 | version = "1.0.5" 48 | description = "A minimal low-level HTTP client." 49 | optional = false 50 | python-versions = ">=3.8" 51 | files = [ 52 | {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, 53 | {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, 54 | ] 55 | 56 | [package.dependencies] 57 | certifi = "*" 58 | h11 = ">=0.13,<0.15" 59 | 60 | [package.extras] 61 | asyncio = ["anyio (>=4.0,<5.0)"] 62 | http2 = ["h2 (>=3,<5)"] 63 | socks = ["socksio (==1.*)"] 64 | trio = ["trio (>=0.22.0,<0.26.0)"] 65 | 66 | [[package]] 67 | name = "httpx" 68 | version = "0.27.0" 69 | description = "The next generation HTTP client." 70 | optional = false 71 | python-versions = ">=3.8" 72 | files = [ 73 | {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, 74 | {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, 75 | ] 76 | 77 | [package.dependencies] 78 | anyio = "*" 79 | certifi = "*" 80 | httpcore = "==1.*" 81 | idna = "*" 82 | sniffio = "*" 83 | socksio = {version = "==1.*", optional = true, markers = "extra == \"socks\""} 84 | 85 | [package.extras] 86 | brotli = ["brotli", "brotlicffi"] 87 | cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] 88 | http2 = ["h2 (>=3,<5)"] 89 | socks = ["socksio (==1.*)"] 90 | 91 | [[package]] 92 | name = "idna" 93 | version = "3.7" 94 | description = "Internationalized Domain Names in Applications (IDNA)" 95 | optional = false 96 | python-versions = ">=3.5" 97 | files = [ 98 | {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, 99 | {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, 100 | ] 101 | 102 | [[package]] 103 | name = "markdown-it-py" 104 | version = "3.0.0" 105 | description = "Python port of markdown-it. Markdown parsing, done right!" 106 | optional = false 107 | python-versions = ">=3.8" 108 | files = [ 109 | {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, 110 | {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, 111 | ] 112 | 113 | [package.dependencies] 114 | mdurl = ">=0.1,<1.0" 115 | 116 | [package.extras] 117 | benchmarking = ["psutil", "pytest", "pytest-benchmark"] 118 | code-style = ["pre-commit (>=3.0,<4.0)"] 119 | compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] 120 | linkify = ["linkify-it-py (>=1,<3)"] 121 | plugins = ["mdit-py-plugins"] 122 | profiling = ["gprof2dot"] 123 | rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] 124 | testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] 125 | 126 | [[package]] 127 | name = "mdurl" 128 | version = "0.1.2" 129 | description = "Markdown URL utilities" 130 | optional = false 131 | python-versions = ">=3.7" 132 | files = [ 133 | {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, 134 | {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, 135 | ] 136 | 137 | [[package]] 138 | name = "pygments" 139 | version = "2.17.2" 140 | description = "Pygments is a syntax highlighting package written in Python." 141 | optional = false 142 | python-versions = ">=3.7" 143 | files = [ 144 | {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, 145 | {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, 146 | ] 147 | 148 | [package.extras] 149 | plugins = ["importlib-metadata"] 150 | windows-terminal = ["colorama (>=0.4.6)"] 151 | 152 | [[package]] 153 | name = "rich" 154 | version = "13.7.1" 155 | description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" 156 | optional = false 157 | python-versions = ">=3.7.0" 158 | files = [ 159 | {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, 160 | {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, 161 | ] 162 | 163 | [package.dependencies] 164 | markdown-it-py = ">=2.2.0" 165 | pygments = ">=2.13.0,<3.0.0" 166 | 167 | [package.extras] 168 | jupyter = ["ipywidgets (>=7.5.1,<9)"] 169 | 170 | [[package]] 171 | name = "six" 172 | version = "1.16.0" 173 | description = "Python 2 and 3 compatibility utilities" 174 | optional = false 175 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 176 | files = [ 177 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 178 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 179 | ] 180 | 181 | [[package]] 182 | name = "sniffio" 183 | version = "1.3.1" 184 | description = "Sniff out which async library your code is running under" 185 | optional = false 186 | python-versions = ">=3.7" 187 | files = [ 188 | {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, 189 | {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, 190 | ] 191 | 192 | [[package]] 193 | name = "socksio" 194 | version = "1.0.0" 195 | description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." 196 | optional = false 197 | python-versions = ">=3.6" 198 | files = [ 199 | {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"}, 200 | {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"}, 201 | ] 202 | 203 | [[package]] 204 | name = "user-agent" 205 | version = "0.1.10" 206 | description = "User-Agent generator" 207 | optional = false 208 | python-versions = "*" 209 | files = [ 210 | {file = "user_agent-0.1.10.tar.gz", hash = "sha256:b86537cb2a9d3bda0e2afcc654ec15b383502836877a67520654acadf73f1723"}, 211 | ] 212 | 213 | [package.dependencies] 214 | six = "*" 215 | 216 | [metadata] 217 | lock-version = "2.0" 218 | python-versions = "^3.11" 219 | content-hash = "192b3c9aab51a9356faedf7e2cd78ab09e77fc6068bdcd40ccfd89b2d93b25f7" 220 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "recapv3" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["x404xx "] 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.11" 10 | user-agent = "^0.1.10" 11 | rich = "^13.7.1" 12 | httpx = {extras = ["socks"], version = "^0.27.0"} 13 | 14 | 15 | [build-system] 16 | requires = ["poetry-core"] 17 | build-backend = "poetry.core.masonry.api" 18 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | anyio==4.3.0 ; python_version >= "3.11" and python_version < "4.0" 2 | certifi==2024.2.2 ; python_version >= "3.11" and python_version < "4.0" 3 | h11==0.14.0 ; python_version >= "3.11" and python_version < "4.0" 4 | httpcore==1.0.5 ; python_version >= "3.11" and python_version < "4.0" 5 | httpx[socks]==0.27.0 ; python_version >= "3.11" and python_version < "4.0" 6 | idna==3.7 ; python_version >= "3.11" and python_version < "4.0" 7 | markdown-it-py==3.0.0 ; python_version >= "3.11" and python_version < "4.0" 8 | mdurl==0.1.2 ; python_version >= "3.11" and python_version < "4.0" 9 | pygments==2.17.2 ; python_version >= "3.11" and python_version < "4.0" 10 | rich==13.7.1 ; python_version >= "3.11" and python_version < "4.0" 11 | six==1.16.0 ; python_version >= "3.11" and python_version < "4.0" 12 | sniffio==1.3.1 ; python_version >= "3.11" and python_version < "4.0" 13 | socksio==1.0.0 ; python_version >= "3.11" and python_version < "4.0" 14 | user-agent==0.1.10 ; python_version >= "3.11" and python_version < "4.0" 15 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | from .captcha_solver import CaptchaSolver 2 | from .rich_console import RichConsole 3 | from .http_client import HttpClient 4 | -------------------------------------------------------------------------------- /src/captcha_solver.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import re 3 | from dataclasses import dataclass 4 | from urllib.parse import urlparse 5 | 6 | from rich.box import HEAVY 7 | from rich.table import Table 8 | from rich.traceback import install as traceback_install 9 | 10 | from .rich_console import RichConsole 11 | 12 | traceback_install(show_locals=True, word_wrap=True) 13 | 14 | 15 | @dataclass 16 | class CaptchaData: 17 | """ 18 | Represents the data structure for captcha-related information. 19 | 20 | Args: 21 | sitekey: The sitekey used in the captcha challenge. 22 | page_action: The action related to the captcha challenge. 23 | api_type: The type of API used in the captcha challenge. 24 | constructed_url: The URL constructed for the captcha challenge. 25 | co_value: The value encoded for the captcha challenge. 26 | anchor_url: The URL of the anchor used in the captcha challenge. 27 | anchor_token: The token associated with the anchor in the captcha challenge. 28 | captcha_token: The token generated after solving the captcha challenge. 29 | """ 30 | 31 | sitekey: str 32 | page_action: str 33 | api_type: str 34 | constructed_url: str 35 | co_value: str 36 | anchor_url: str 37 | anchor_token: str 38 | captcha_token: str 39 | 40 | 41 | class CaptchaSolver: 42 | """ 43 | Initializes the captcha solver with the base URL and verbosity option. 44 | 45 | Args: 46 | base_url (str): The base URL for the captcha solver. 47 | verbose (bool, optional): Whether to print Captcha data information. Defaults to false. 48 | 49 | Methods: 50 | - extract_data: Extracts data from a URL response based on a given pattern. 51 | - get_sitekey: Gets the sitekey from the response text. 52 | - get_page_action: Extracts the action value from the page content. 53 | - construct_url: Constructs a URL with a specified port based on the base URL. 54 | - encode_co: Encodes the constructed URL. 55 | - get_api_type: Get the API type. 56 | - construct_anchor: Construct the anchor URL for a Google reCAPTCHA. 57 | - get_anchor_token: Get the anchor token from the provided anchor URL. 58 | - build_payload: Build a payload string for a CAPTCHA request. 59 | - get_captcha_token: Get the CAPTCHA token for a given CAPTCHA challenge. 60 | - solve_captcha: Solves a captcha challenge and returns the captcha token. 61 | 62 | Raises: 63 | ValueError: If sitekey is not found. 64 | """ 65 | 66 | CAPTCHA_URL = "https://www.google.com/recaptcha" 67 | 68 | def __init__(self, base_url, verbose=False): 69 | """ 70 | Initializes the captcha solver with the base URL and verbosity option. 71 | 72 | Args: 73 | base_url (str): The base URL for the captcha solver. 74 | verbose (bool, optional): Whether to print Captcha data information. Defaults to false. 75 | """ 76 | self._base_url = base_url 77 | self._verbose = verbose 78 | self.sitekey = None 79 | self.page_action = None 80 | 81 | def _extract_data(self, client, url, pattern): 82 | """ 83 | Extracts data from a URL response based on a given pattern. 84 | 85 | Args: 86 | client: The HTTP client used to make the request. 87 | url (str): The URL to fetch the data from. 88 | pattern (str): The regex pattern to search for in the response. 89 | 90 | Returns: 91 | str or None: The extracted data if found, otherwise None. 92 | """ 93 | response = client.get(url) 94 | if match := re.search(pattern, response.text): 95 | return match[1] or match[2] or match[3] or None 96 | return None 97 | 98 | def _get_sitekey(self, client): 99 | """ 100 | Gets the sitekey from the response text. 101 | 102 | Args: 103 | client: The HTTP client used to make the request. 104 | 105 | Returns: 106 | str or None: The sitekey extracted from the response text, or None if not found. 107 | """ 108 | pattern = r"render=(.*?)'|execute\('(.*?)'|'(.*?)&" 109 | return self._extract_data(client, self._base_url, pattern) 110 | 111 | def _get_page_action(self, client): 112 | """ 113 | Extracts the action value from the page content. 114 | 115 | Args: 116 | client: The HTTP client used to make the request. 117 | 118 | Returns: 119 | str: The action value extracted from the page content. 120 | """ 121 | pattern = r"action: '(.*?)'" 122 | return self._extract_data(client, self._base_url, pattern) 123 | 124 | def _construct_url(self, port="443"): 125 | """ 126 | Constructs a URL with a specified port based on the base URL. 127 | 128 | Args: 129 | port (str): The port number to append to the URL. Defaults to "443". 130 | 131 | Returns: 132 | str: The constructed URL with the specified port. 133 | """ 134 | parsed_url = urlparse(self._base_url) 135 | return f"{parsed_url.scheme}://{parsed_url.netloc}:{port}" 136 | 137 | def _encode_co(self, constructed_url): 138 | """ 139 | Encodes the constructed URL. 140 | 141 | Args: 142 | constructed_url (str): The URL to be encoded. 143 | 144 | Returns: 145 | str: The encoded URL. 146 | """ 147 | encoded_url = base64.b64encode(constructed_url.encode()).decode() 148 | return encoded_url.rstrip("=") + "." * ( 149 | len(encoded_url) - len(encoded_url.rstrip("=")) 150 | ) 151 | 152 | def _get_api_type(self, client): 153 | """ 154 | Get the API type. 155 | 156 | Args: 157 | client: The HTTP client used to make the request. 158 | 159 | Returns: 160 | str or None: The API type extracted from the response text, or None if not found. 161 | """ 162 | pattern = r"/recaptcha/(api|enterprise)\." 163 | api_type = self._extract_data(client, self._base_url, pattern) 164 | return "api2" if api_type == "api" else "enterprise" 165 | 166 | def _construct_anchor(self, sitekey, co_value, api_type): 167 | """ 168 | Construct the anchor URL for a Google reCAPTCHA. 169 | 170 | Args: 171 | sitekey (str): The sitekey for the reCAPTCHA. 172 | co_value (str): The value of the 'co' parameter. 173 | api_type (str): The type of reCAPTCHA API. 174 | 175 | Returns: 176 | str: The constructed anchor URL for the reCAPTCHA. 177 | """ 178 | return f"{self.CAPTCHA_URL}/{api_type}/anchor?ar=1&k={sitekey}&co={co_value}&hl=en&size=invisible" 179 | 180 | def _get_anchor_token(self, client, anchor_url): 181 | """ 182 | Get the anchor token from the provided anchor URL. 183 | 184 | Args: 185 | client: The HTTP client used to make the request. 186 | anchor_url (str): The URL to fetch the anchor token from. 187 | 188 | Returns: 189 | str or None: The anchor token extracted from the response text, or None if not found. 190 | """ 191 | pattern = r'recaptcha-token" value="(.*?)"' 192 | return self._extract_data(client, anchor_url, pattern) 193 | 194 | def _build_payload(self, anchor_token, co_value, sitekey): 195 | """ 196 | Build a payload string for a CAPTCHA request. 197 | 198 | Args: 199 | anchor_token (str): The anchor token for the CAPTCHA request. 200 | co_value (str): The co value for the CAPTCHA request. 201 | sitekey (str): The sitekey for the CAPTCHA request. 202 | 203 | Returns: 204 | str: A formatted payload string for the CAPTCHA request. 205 | """ 206 | return f"reason=q&c={anchor_token}&k={sitekey}&co={co_value}" 207 | 208 | def _get_captcha_token(self, client, anchor_token, co_value, sitekey, api_type): 209 | """ 210 | Get the CAPTCHA token for a given CAPTCHA challenge. 211 | 212 | Args: 213 | client: The HTTP client used to make the request. 214 | anchor_token (str): The anchor token for the CAPTCHA request. 215 | co_value (str): The co value for the CAPTCHA request. 216 | sitekey (str): The sitekey for the CAPTCHA request. 217 | api_type (str): The type of reCAPTCHA API to use. 218 | 219 | Returns: 220 | str: The CAPTCHA token extracted from the response, or None if not found. 221 | """ 222 | response = client.post( 223 | f"{self.CAPTCHA_URL}/{api_type}/reload", 224 | headers={"Content-Type": "application/x-www-form-urlencoded"}, 225 | params={"k": sitekey}, 226 | data=self._build_payload(anchor_token, co_value, sitekey), 227 | ) 228 | match = re.search(r'"rresp","(.*?)"', response.text) 229 | return match[1] if match else None 230 | 231 | def solve_captcha(self, client): 232 | """ 233 | Solves a captcha challenge using the provided client. 234 | 235 | Args: 236 | client: The HTTP client used to make the request. 237 | 238 | Returns: 239 | str: The captcha token generated after solving the captcha challenge. 240 | 241 | Raises: 242 | ValueError: If the sitekey is not found. 243 | """ 244 | self.sitekey = self._get_sitekey(client) 245 | if not self.sitekey: 246 | raise ValueError("Sitekey not found!") 247 | self.page_action = self._get_page_action(client) 248 | api_type = self._get_api_type(client) 249 | if not api_type: 250 | raise ValueError( 251 | f"API Type not found! Please check the response text from {self._base_url}" 252 | ) 253 | constructed_url = self._construct_url() 254 | co_value = self._encode_co(constructed_url) 255 | anchor_url = self._construct_anchor(self.sitekey, co_value, api_type) 256 | anchor_token = self._get_anchor_token(client, anchor_url) 257 | captcha_token = self._get_captcha_token( 258 | client, anchor_token, co_value, self.sitekey, api_type 259 | ) 260 | 261 | if self._verbose: 262 | captcha_data = CaptchaData( 263 | sitekey=self.sitekey, 264 | page_action=self.page_action, 265 | api_type=api_type, 266 | constructed_url=constructed_url, 267 | co_value=co_value, 268 | anchor_url=anchor_url, 269 | anchor_token=anchor_token, 270 | captcha_token=captcha_token, 271 | ) 272 | table = Table(show_header=False, box=HEAVY, border_style="purple") 273 | table.add_column("Field", style="medium_purple") 274 | table.add_column("Value", style="dodger_blue2") 275 | for field, value in captcha_data.__dict__.items(): 276 | table.add_row(field, value) 277 | 278 | RichConsole.print(table) 279 | 280 | return captcha_token 281 | -------------------------------------------------------------------------------- /src/http_client.py: -------------------------------------------------------------------------------- 1 | from random import uniform 2 | 3 | import httpx 4 | from user_agent import generate_user_agent as user_agent 5 | 6 | from .rich_console import RichConsole 7 | 8 | 9 | class LogHandler: 10 | """ 11 | A class for logging HTTP requests and responses. 12 | 13 | Methods: 14 | - log_request: Logs the details of an HTTP request. 15 | - log_response: Logs the details of an HTTP response. 16 | 17 | Args: 18 | - request: The HTTP request object to log in the format " {method} {url} - Waiting for response". 19 | - response: The HTTP response object to log in the format " {status_code}". 20 | 21 | Returns: 22 | - None 23 | """ 24 | 25 | @staticmethod 26 | def log_request(request): 27 | RichConsole.print( 28 | f" {request.method} {request.url} - Waiting for response" 29 | ) 30 | 31 | @staticmethod 32 | def log_response(response): 33 | RichConsole.print(f" {response.status_code}", end="\n\n") 34 | 35 | 36 | class HttpClient(LogHandler): 37 | """ 38 | HttpClient class for making HTTP requests. 39 | 40 | Methods: 41 | __init__: Initialize the client with an optional proxy URL. 42 | __enter__: Enter method for context management. 43 | __exit__: Exit method for context management. 44 | _make_request: Make an HTTP request with error handling. 45 | get: Make a GET request to the specified URL. 46 | post: Make a POST request to the specified URL. 47 | 48 | Returns: 49 | None 50 | """ 51 | 52 | def __init__(self, proxy_url=None, log_handler=False, timeout=uniform(10, 15)): 53 | """ 54 | Initialize the client with an optional proxy URL. 55 | 56 | Args: 57 | proxy_url (str, optional): The proxy URL to use. Defaults to None. 58 | log_handler (bool, optional): Whether to print a log message. Defaults to False. 59 | timeout (float): A random timeout between 10 and 15 seconds will be used. 60 | 61 | Returns: 62 | None 63 | """ 64 | self.base_agent = {"User-Agent": str(user_agent())} 65 | self._client = httpx.Client( 66 | headers=self.base_agent, 67 | proxy=proxy_url, 68 | timeout=timeout, 69 | event_hooks=( 70 | { 71 | "request": [self.log_request], 72 | "response": [self.log_response], 73 | } 74 | if log_handler 75 | else None 76 | ), 77 | ) 78 | 79 | def __enter__(self): 80 | return self 81 | 82 | def __exit__(self, *_): 83 | self._client.close() 84 | 85 | def _make_request(self, method, url, **kwargs): 86 | try: 87 | response = self._client.request(method, url, **kwargs) 88 | response.raise_for_status() 89 | return response 90 | except (httpx.HTTPError, Exception) as exc: 91 | raise exc 92 | 93 | def get(self, url, **kwargs): 94 | return self._make_request("GET", url, **kwargs) 95 | 96 | def post(self, url, **kwargs): 97 | return self._make_request("POST", url, **kwargs) 98 | -------------------------------------------------------------------------------- /src/rich_console.py: -------------------------------------------------------------------------------- 1 | from rich.console import Console 2 | 3 | RichConsole = Console() 4 | --------------------------------------------------------------------------------