├── .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 |
--------------------------------------------------------------------------------