Showing preview only (320K chars total). Download the full file or copy to clipboard to get everything.
Repository: sergey-scat/unicaps
Branch: master
Commit: bd24c99ec4e9
Files: 81
Total size: 298.6 KB
Directory structure:
gitextract_x1qvq890/
├── .github/
│ └── workflows/
│ ├── python-package.yml
│ └── python-publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── acceptance_tests.py
├── examples/
│ ├── README.MD
│ ├── async_capy_puzzle.py
│ ├── async_funcaptcha.py
│ ├── async_geetest.py
│ ├── async_geetest_v4.py
│ ├── async_hcaptcha.py
│ ├── async_image.py
│ ├── async_keycaptcha.py
│ ├── async_recaptcha_v2.py
│ ├── async_recaptcha_v2_enterprise.py
│ ├── async_recaptcha_v2_invisible.py
│ ├── async_recaptcha_v3.py
│ ├── async_text.py
│ ├── capy_puzzle.py
│ ├── funcaptcha.py
│ ├── geetest.py
│ ├── geetest_v4.py
│ ├── hcaptcha.py
│ ├── image.py
│ ├── keycaptcha.py
│ ├── recaptcha_v2.py
│ ├── recaptcha_v2_enterprise.py
│ ├── recaptcha_v2_invisible.py
│ ├── recaptcha_v3.py
│ ├── requirements.txt
│ ├── run_all.py
│ └── text.py
├── requirements-dev.txt
├── requirements.txt
├── setup.cfg
├── setup.py
├── tests/
│ ├── _helpers.py
│ ├── conftest.py
│ ├── data/
│ │ ├── __init__.py
│ │ └── data.py
│ ├── test_captcha_base.py
│ ├── test_image_captcha.py
│ ├── test_proxy.py
│ ├── test_service_module.py
│ └── test_solver.py
└── unicaps/
├── __init__.py
├── __version__.py
├── _captcha/
│ ├── __init__.py
│ ├── base.py
│ ├── capy.py
│ ├── funcaptcha.py
│ ├── geetest.py
│ ├── geetest_v4.py
│ ├── hcaptcha.py
│ ├── image.py
│ ├── keycaptcha.py
│ ├── recaptcha_v2.py
│ ├── recaptcha_v3.py
│ ├── text.py
│ └── tiktok.py
├── _misc/
│ ├── __init__.py
│ └── proxy.py
├── _service/
│ ├── __init__.py
│ ├── anti_captcha.py
│ ├── azcaptcha.py
│ ├── base.py
│ ├── captcha_guru.py
│ ├── cptch_net.py
│ ├── deathbycaptcha.py
│ ├── rucaptcha.py
│ └── twocaptcha.py
├── _solver.py
├── _solver_async.py
├── _transport/
│ ├── __init__.py
│ ├── base.py
│ └── http_transport.py
├── captcha.py
├── common.py
├── exceptions.py
└── proxy.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/python-package.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: Python package
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=12 --max-line-length=100 --statistics --per-file-ignores="tests/conftest.py:E402"
- name: Test with pytest
run: |
pytest ./tests -v
================================================
FILE: .github/workflows/python-publish.yml
================================================
# This workflow will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.
name: Upload Python Package
on:
release:
types: [published]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip setuptools wheel
pip install build
- name: Build package
run: python -m build
- name: Publish package
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
with:
user: __token__
password: ${{ secrets.PYPI_PASSWORD }}
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# Wing Pro
*.wpr
*.wpu
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 Sergey Totmyanin
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
<p align="center">
<img src="https://i.imgur.com/8aQf6On.png" />
</p>
# Unicaps
[](https://pypi.python.org/pypi/unicaps/)
[](https://pypi.python.org/pypi/unicaps/)
[](https://pypi.python.org/pypi/unicaps/)
[](https://www.codefactor.io/repository/github/sergey-scat/unicaps)
Unicaps is a unified Python API for CAPTCHA solving services.
⚠ **PLEASE NOTE**</br>
⚠ A solving service API key is required to use this package!</br>
⚠ The list of the supported services you can find in the table below.
## Key Features
- A unified Python interface that is independent of the service used
- Uses native service protocol/endpoints (eg, no needs in patching _hosts_ file)
- Has both synchronous and asynchronous client
- Supports 10 types of CAPTCHAs
- Supports 6 CAPTCHA solving services
- Written Pythonic way and is intended for humans
## Installation
```pip install -U unicaps```
## Simple Usage Example
```python
>>> from unicaps import CaptchaSolver, CaptchaSolvingService
>>> solver = CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, api_key="<PLACE_YOUR_API_KEY_HERE>")
>>> solver.get_balance()
2.84161
>>> solved = solver.solve_image_captcha(open("captcha.jpg", "rb"), is_phrase=False, is_case_sensitive=True)
>>> solved.solution.text
'w93Bx'
>>> solved.cost
0.00078
>>> solved.report_good()
True
```
## Asynchronous Example
```python
import asyncio
from pathlib import Path
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService
API_KEY = '<PLACE_YOUR_API_KEY_HERE>'
async def main():
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
solved = await solver.solve_image_captcha(
Path("captcha.jpg"),
is_phrase=False,
is_case_sensitive=True
)
print(f'CAPTCHA text: {solved.solution.text}')
await solved.report_good()
if __name__ == '__main__':
asyncio.run(main())
```
## Supported CAPTCHAs / Services
| CAPTCHA➡ \ Service⬇| Image | Text | [reCAPTCHA v2](https://developers.google.com/recaptcha/docs/display) | [reCAPTCHA v3](https://developers.google.com/recaptcha/docs/v3) | [FunCaptcha](https://funcaptcha.com/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC) | [KeyCAPTCHA](https://www.keycaptcha.com/) | [Geetest](https://www.geetest.com/en/demo) | [Geetest v4](https://www.geetest.com/en/demo) | [hCaptcha](https://www.hcaptcha.com/) | [Capy](https://www.capy.me/)
| ------------- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |
| [azcaptcha.com](https://azcaptcha.com) | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |
| [deathbycaptcha.com](https://www.deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
### Image CAPTCHA
| Service | Regular | Case Sensitive | Phrase | Numbers only | Letters only | Math | Length | Language | Comment for worker
| ------------- | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Cyrillic/Latin | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Latin | ✅ |
| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Latin | ✅ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Latin | ✅ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Latin | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Cyrillic/Latin | ✅ |
### Text CAPTCHA
<details closed>
<summary>What is this?</summary>
<i>Text Captcha is a type of captcha that is represented as text and doesn't contain images. Usually you have to answer a question to pass the verification.
For example: "If tomorrow is Saturday, what day is today?".</i>
</details>
| Service | Language |
| ------------- | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | English, Russian |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ❌ |
| [azcaptcha.com](https://azcaptcha.com/) | ❌ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | English, Russian |
### reCAPTCHA v2
| Service | Regular | Invisible | Enterprise | Google service<sup>1</sup> | Proxy<sup>2</sup> | Cookies<sup>3</sup> | User-Agent<sup>4</sup> |
| ------------- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
<sup>1</sup> Support of solving reCAPTCHA on Google services (e.g. Google Search) </br>
<sup>2</sup> Support of solving via proxy server </br>
<sup>3</sup> Support of passing custom cookies </br>
<sup>4</sup> Support of passing custom User-Agent header </br>
### reCAPTCHA v3
| Service | Regular | Enterprise | Proxy | Cookies | User-Agent |
| ------------- | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ❌ | ❌ | ❌ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ❌ | ❌ | ❌ |
| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ❌ | ✅ | ❌ | ❌ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ✅ | ✅ | ✅ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ✅ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ❌ | ❌ | ❌ |
### FunCaptcha (Arkose Labs)
| Service | Regular | Data (BLOB) | Proxy | Cookies | User-Agent |
| ------------- | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ✅ | ❌ | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ❌ | ✅ |
| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ✅ | ✅ | ❌ | ✅ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ | ❌ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ✅ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ❌ | ✅ |
### KeyCAPTCHA
| Service | Regular | Proxy | Cookies | User-Agent |
| ------------- | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ❌ | ❌ | ❌ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ❌ | ❌ | ❌ | ❌ |
| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ❌ | ❌ | ❌ |
### Geetest
| Service | Regular | API server | GetLib | Proxy | Cookies | User-Agent |
| ------------- | :---: | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| [cap.guru](https://captcha.guru/ru/reg/?ref=127872) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |
### Geetest v4
| Service | Regular | Proxy | Cookies | User-Agent |
| ------------- | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ❌ | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ❌ | ✅ |
| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ❌ | ✅ |
### hCaptcha
| Service | Regular | Invisible | Custom Data | Proxy | Cookies | User-Agent |
| ------------- | :---: | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |
| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |
### Capy Puzzle
| Service | Regular | API server | Proxy | Cookies | User-Agent |
| ------------- | :---: | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ✅ | ❌ | ❌ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ❌ | ❌ | ❌ | ❌ | ❌ |
| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ | ❌ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ | ❌ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ❌ | ❌ |
## Supported Proxy types
| Service | HTTP | HTTPS | SOCKS 4 | SOCKS 5 |
| ------------- | :---: | :---: | :---: | :---: |
| [2captcha.com](http://2captcha.com/?from=8754088) | ✅ | ✅ | ✅ | ✅ |
| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ |
| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ✅ | ✅ | ✅ |
| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ✅ | ✅ | ✅ |
| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ❌ | ❌ |
| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ |
## How to...
### Common
<details>
<summary>Get balance</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.ANTI_CAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
balance = solver.get_balance()
```
</details>
<details>
<summary>Get service status (is the service is up?)</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.ANTI_CAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# get status of the service (True - everything is Okay, False - probably the service is down)
status = solver.get_status()
```
</details>
<details>
<summary>Get technical details after solving</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# init captcha solver and solve the captcha
with CaptchaSolver(CaptchaSolvingService.ANTI_CAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
solved = solver.solve_...(...)
# get cost of the solving
cost = solved.cost
# get cookies (if any)
cookies = solved.cookies
# report good captcha
solved.report_good()
# report bad captcha
solved.report_bad()
# get solving start time
start_time = solved.start_time
# get solving end time
end_time = solved.end_time
```
</details>
### CAPTCHAs
<details>
<summary>Solve Image CAPTCHA</summary>
```python
import pathlib
from unicaps import CaptchaSolver, CaptchaSolvingService
from unicaps.common import CaptchaCharType, CaptchaAlphabet
# image file: it can be a Path, file-object or bytes.
image_file = pathlib.Path(r'/tmp/captcha.png')
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_image_captcha(
image=image_file,
char_type=CaptchaCharType.ALPHA, # optional
is_phrase=False, # optional
is_case_sensitive=True, # optional
is_math=False, # optional
min_len=4, # optional
max_len=6, # optional
alphabet=CaptchaAlphabet.LATIN, # optional
comment='Type RED letters only' # optional
)
# get CAPTCHA text
token = solved.solution.text
```
</details>
<details>
<summary>Solve reCAPTCHA v2</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get page URL and site_key from your page
page_url = ...
site_key = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_recaptcha_v2(
site_key=site_key,
page_url=page_url,
data_s='<data-s value>', # optional
api_domain='<"google.com" or "recaptcha.net">' # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve reCAPTCHA v2 Invisible</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get page url and site_key from your page
page_url = ...
site_key = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_recaptcha_v2(
site_key=site_key,
page_url=page_url,
is_invisible=True,
data_s='<data-s value>', # optional
api_domain='<"google.com" or "recaptcha.net">' # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve reCAPTCHA v2 Enterprise</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get page URL, site_key and data_s from your page
page_url = ...
site_key = ...
data_s = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_recaptcha_v2(
site_key=site_key,
page_url=page_url,
is_enterprise=True,
data_s=data_s, # optional
api_domain='<"google.com" or "recaptcha.net">' # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve reCAPTCHA v3</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
page_url = ...
site_key = ...
action = ...
min_score = 0.7
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_recaptcha_v3(
site_key=site_key,
page_url=page_url,
action=action, # optional
min_score=min_score, # optional
api_domain='<"google.com" or "recaptcha.net">' # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve reCAPTCHA v3 Enterprise</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
page_url = ...
site_key = ...
action = ...
min_score = 0.7
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_recaptcha_v3(
site_key=site_key,
page_url=page_url,
is_enterprise=True,
action=action, # optional
min_score=min_score, # optional
api_domain='<"google.com" or "recaptcha.net">' # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve hCaptcha</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
page_url = ...
site_key = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_hcaptcha(
site_key=site_key,
page_url=page_url,
api_domain=<"hcaptcha.com" or "js.hcaptcha.com"> # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve hCaptcha Invisible</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
page_url = ...
site_key = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_hcaptcha(
site_key=site_key,
page_url=page_url,
is_invisible=True,
api_domain=<"hcaptcha.com" or "js.hcaptcha.com"> # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve FunCaptcha</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
public_key = ...
page_url = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_funcaptcha(
public_key=public_key,
page_url=page_url,
service_url='<value of surl parameter>', # optional
blob='<value of data[blob] parameter>' # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve KeyCaptcha</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
page_url = ...
user_id = ...
session_id = ...
ws_sign = ...
ws_sign2 = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_keycaptcha(
page_url=page_url,
user_id=user_id,
session_id=session_id,
ws_sign=ws_sign,
ws_sign2=ws_sign2
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve Geetest</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
page_url = ...
gt_key = ...
challenge = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_geetest(
page_url=page_url,
gt_key=gt_key,
challenge=challenge,
api_server='<value of api_server parameter>' # optional
)
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Solve Geetest v4</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
page_url = ...
captcha_id = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_geetest_v4(
page_url=page_url,
captcha_id=captcha_id
)
# get solution data
lot_number = solved.solution.lot_number
pass_token = solved.solution.pass_token
gen_time = solved.solution.gen_time
captcha_output = solved.solution.captcha_output
```
</details>
<details>
<summary>Solve Capy Puzzle</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
# get CAPTCHA params from the target page/site
site_key = ...
page_url = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_capy_puzzle(
site_key=site_key,
page_url=page_url,
api_server='<for example "https://api.capy.me">', # optional
challenge_type='<"puzzle" or "avatar">' # optional
)
# get solution data
captchakey = solved.solution.captchakey
challengekey = solved.solution.challengekey
answer = solved.solution.answer
```
</details>
<details>
<summary>Solve a text CAPTCHA</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
from unicaps.common import CaptchaAlphabet, WorkerLanguage
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_text_captcha(
text='Si mañana es domingo, ¿qué día es hoy?',
alphabet=CaptchaAlphabet.LATIN, # optional
language=WorkerLanguage.SPANISH # optional
)
# get answer
answer = solved.solution.text # Sábado
```
</details>
### Error handling
<details>
<summary>Catch exceptions</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
try:
solved = solver.solve_recaptcha_v2(
site_key=site_key,
page_url=page_url
)
except exceptions.AccessDeniedError: # wrong API key or the current IP is banned
pass
except exceptions.LowBalanceError: # low balance
pass
except exceptions.ServiceTooBusy: # no available slots to solve CAPTCHA
pass
except exceptions.SolutionWaitTimeout: # haven't received a solution within N minutes
pass
except exceptions.TooManyRequestsError: # request limit exceeded
pass
except exceptions.BadInputDataError: # bad CAPTCHA data (bad image, wrong URL, etc.)
pass
except exceptions.UnableToSolveError: # CAPTCHA unsolvable
pass
except exceptions.ProxyError: # bad proxy
pass
except exceptions.NetworkError: # network connection error
pass
else:
# get response token
token = solved.solution.token
```
</details>
### Misc
<details>
<summary>Create a task and wait for the result</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
from unicaps.captcha import RecaptchaV2
# get page URL and site_key from your page
page_url = ...
site_key = ...
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# create a task
task = solver.create_task(
RecaptchaV2(site_key, page_url)
)
# print task ID
print(task.task_id)
# wait for task to be completed
solved = task.wait()
# get response token
token = solved.solution.token
```
</details>
<details>
<summary>Add proxy, cookies and User-Agent</summary>
```python
from unicaps import CaptchaSolver, CaptchaSolvingService
from unicaps.proxy import ProxyServer
# get page URL and site_key from your page
page_url = ...
site_key = ...
proxy = 'http://user:password@domain.com:8080'
user_agent = '<USER AGENT STRING>'
cookies = {'name': 'value', ...}
# init captcha solver
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, "<PLACE YOUR API KEY HERE>") as solver:
# solve CAPTCHA
solved = solver.solve_recaptcha_v2(
site_key=site_key,
page_url=page_url,
proxy=ProxyServer(proxy),
user_agent=user_agent,
cookies=cookies
)
# get response token
token = solved.solution.token
```
</details>
## Real-life code examples
[Examples](https://github.com/sergey-scat/unicaps/tree/master/examples)
## Buy me a coffee
https://www.buymeacoffee.com/sergey.scat
================================================
FILE: acceptance_tests.py
================================================
"""
Acceptance tests
"""
import asyncio
import logging
import os
from importlib import import_module
from unicaps import CaptchaSolver, AsyncCaptchaSolver
from unicaps.__version__ import __version__
logging.basicConfig(level=logging.INFO)
SERVICES = {
'2captcha.com': 'API_KEY_2CAPTCHA',
'anti-captcha.com': 'API_KEY_ANTICAPTCHA',
'azcaptcha.com': 'API_KEY_AZCAPTCHA',
'captcha.guru': 'API_KEY_CAPTCHA_GURU',
'cptch.net': 'API_KEY_CPTCH_NET',
'deathbycaptcha.com': 'API_KEY_DEATHBYCAPTCHA'
}
EXAMPLES = [
'image',
'recaptcha_v2',
'recaptcha_v2_invisible',
'recaptcha_v2_enterprise',
'recaptcha_v3',
'hcaptcha',
'keycaptcha',
'geetest',
'geetest_v4',
'capy_puzzle',
'text',
'funcaptcha'
]
def main():
for service_name, env_var_name in SERVICES.items():
api_key = os.getenv(env_var_name)
if not api_key:
logging.error(
'Unable to read API key for the "%s" service. '
'The environment variable "%s" doesn\'t exist!',
service_name,
env_var_name
)
continue
logging.info('######### Service: %s #########', service_name)
with CaptchaSolver(service_name, api_key) as solver:
status = solver.get_status()
balance = solver.get_balance()
logging.info('Status: %s. Balance: %.2f', 'OK' if status else 'ERROR', balance)
for example_name in EXAMPLES:
logging.info('Current module: %s', example_name)
module = import_module(f'examples.{example_name}')
try:
module.run(solver)
except Exception:
logging.exception('%s run exception', module)
async def async_main():
for service_name, env_var_name in SERVICES.items():
api_key = os.getenv(env_var_name)
if not api_key:
logging.error(
'Unable to read API key for the "%s" service. '
'The environment variable "%s" doesn\'t exist!',
service_name,
env_var_name
)
continue
logging.info('######### Service: %s #########', service_name)
async with AsyncCaptchaSolver(service_name, api_key) as async_solver:
status = await async_solver.get_status()
balance = await async_solver.get_balance()
logging.info(
'Status: %s. Balance: %.2f',
'OK' if status else 'ERROR',
balance
)
for example_name in EXAMPLES:
logging.info('Current module: %s', example_name)
module = import_module(f'examples.async_{example_name}')
try:
await module.run(async_solver)
except Exception:
logging.exception('%s run exception', module)
if __name__ == '__main__':
main()
asyncio.run(async_main(), debug=False)
================================================
FILE: examples/README.MD
================================================
# Real-life examples
Here are some real-life examples of solving captchas, both with a synchronous client and asynchronous.
By default, all examples use 2captcha.com.
## Prerequisites
- The `httpx` (with HTTP/2 support) and `lxml` packages must be installed before running the examples:
```pip install httpx[http2] lxml```
- You must also specify your API key:
- either through an environment variable (`API_KEY_2CAPTCHA`);
- or directly in the code (replace the `<PLACE_YOUR_API_KEY_HERE>` line with the key value).
- For the reCAPTCHA v2 Enterprise examples, you must specify the proxy address (in the format `http://<LOGIN>:<PASSWORD>@<PROXY_ADDRESS>:<PORT>`) using the `HTTP_PROXY_SERVER` environment variable.
## Proxy usage example
See examples on reCAPTCHA v2 Enterprise.
================================================
FILE: examples/async_capy_puzzle.py
================================================
"""
Capy Puzzle CAPTCHA solving example
"""
import asyncio
import os
from urllib.parse import urlparse, parse_qs
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://www.capy.me/products/puzzle_captcha/'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
USER_AGENT = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Solve Capy Puzzle CAPTCHA """
# create an HTTP2 session
async with httpx.AsyncClient(http2=True,
headers={'User-Agent': USER_AGENT}) as session:
# open page and extract CAPTCHA URL
response = await session.get(URL)
page = html.document_fromstring(response.text)
capy_url = page.xpath('//script[contains(@src, "/puzzle/get_js/")]')[0].attrib['src']
capy_url_parsed = urlparse(capy_url)
api_server = f'{capy_url_parsed.scheme}://{capy_url_parsed.hostname}'
site_key = parse_qs(capy_url_parsed.query)['k'][0]
# solve Capy Puzzle CAPTCHA
try:
solved = await solver.solve_capy_puzzle(
site_key=site_key,
page_url=URL,
api_server=api_server,
user_agent=USER_AGENT
)
except exceptions.UnicapsException as exc:
print(f'Capy Puzzle CAPTCHA solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = await session.post(
URL,
data={
'capy_captchakey': solved.solution.captchakey,
'capy_challengekey': solved.solution.challengekey,
'capy_answer': solved.solution.answer
}
)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//div[@class="result success"]'):
print('The Capy Puzzle CAPTCHA has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The Capy Puzzle CAPTCHA has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_funcaptcha.py
================================================
"""
FunCaptcha solving example
"""
import asyncio
import os
import re
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://client-demo.arkoselabs.com/solo-animals'
URL_VERIFY = 'https://client-demo.arkoselabs.com/solo-animals/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Load and solve FunCaptcha """
# create an HTTP2 session
async with httpx.AsyncClient(http2=True) as session:
# open page and extract CAPTCHA URL
response = await session.get(URL)
page = html.document_fromstring(response.text)
# extract Public Key and Service URL values
script = page.xpath('//script[contains(text(), "public_key")]')[0].text
regexp = re.search(
r'public_key: ?"([0-9A-Z-]+)",\s*surl: ?"(.*)",',
script
)
public_key = regexp.group(1)
service_url = regexp.group(2)
# solve FunCaptcha
try:
solved = await solver.solve_funcaptcha(
public_key=public_key,
page_url=URL,
service_url=service_url
)
except exceptions.UnicapsException as exc:
print(f'FunCaptcha solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = await session.post(
URL_VERIFY,
data={
'name': 'test',
'verification-token': solved.solution.token,
'fc-token': solved.solution.token
}
)
# check the result
if '<h3>Solved!</h3>' in response.text:
print('The FunCaptcha has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The FunCaptcha has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_geetest.py
================================================
"""
GeeTest solving example
"""
import asyncio
import os
import time
import httpx
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://2captcha.com/demo/geetest'
INIT_PARAMS_URL = 'https://2captcha.com/api/v1/captcha-demo/gee-test/init-params?t={ms}'
URL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Solve GeeTest """
# create an HTTP2 session
async with httpx.AsyncClient(http2=True) as session:
# open page and extract CAPTCHA URL
response = await session.get(INIT_PARAMS_URL.format(ms=int(time.time() * 1000)))
init_params = response.json()
# solve GeeTest
try:
solved = await solver.solve_geetest(
page_url=URL,
gt_key=init_params['gt'],
challenge=init_params['challenge']
)
except exceptions.UnicapsException as exc:
print(f'GeeTest solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = await session.post(
URL_VERIFY,
json={
'geetest_challenge': solved.solution.challenge,
'geetest_seccode': solved.solution.seccode,
'geetest_validate': solved.solution.validate
}
)
# check the result
if response.json().get('success'):
print('The GeeTest has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The GeeTest has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_geetest_v4.py
================================================
"""
GeeTest v4 solving example
"""
import asyncio
import os
import re
from urllib.parse import urljoin
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://2captcha.com/demo/geetest-v4'
URL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test-v4/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Solve GeeTest v4 """
# create an HTTP2 session
async with httpx.AsyncClient(http2=True) as session:
# open page and extract CAPTCHA URL
response = await session.get(URL)
page = html.document_fromstring(response.text)
captcha_js_url = page.xpath(
'//script[@data-chunk="pages-CaptchaDemo" and '
'starts-with(@src, "/dist/web/pages-CaptchaDemo.")]'
)[0].attrib['src']
captcha_js_url = urljoin(URL, captcha_js_url)
# load pages-CaptchaDemo js-file and extract captcha ID
response = await session.get(captcha_js_url)
regexp = re.search(
r'window\.initGeetest4\(\{\s*captchaId:\s?"([0-9a-z]+)"',
response.text
)
# solve GeeTest v4
try:
solved = await solver.solve_geetest_v4(
page_url=URL,
captcha_id=regexp.group(1)
)
except exceptions.UnicapsException as exc:
print(f'GeeTestV4 solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = await session.post(
URL_VERIFY,
json={
'captcha_id': solved.solution.captcha_id,
'lot_number': solved.solution.lot_number,
'pass_token': solved.solution.pass_token,
'gen_time': solved.solution.gen_time,
'captcha_output': solved.solution.captcha_output
}
)
# check the result
if response.json().get('result') == 'success':
print('The GeeTest v4 has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The GeeTest v4 has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_hcaptcha.py
================================================
# -*- coding: UTF-8 -*-
"""
hCaptcha solving example
"""
import asyncio
import os
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://accounts.hcaptcha.com/demo'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Solve hCaptcha """
# make a session and go to URL
async with httpx.AsyncClient(http2=True) as session:
response = await session.get(URL)
# parse page and get site-key
page = html.document_fromstring(response.text)
site_key = page.cssselect('.h-captcha')[0].attrib['data-sitekey']
# parse form data
page_form_data = page.xpath('//form//input')
form_data = {}
for input_data in page_form_data:
if input_data.xpath('@name'):
form_data[input_data.xpath('@name')[0]] = next(
iter(input_data.xpath('@value')),
None
)
# solve hCaptcha
try:
solved = await solver.solve_hcaptcha(
site_key=site_key,
page_url=URL
)
except exceptions.UnicapsException as exc:
print(f'hCaptcha solving exception: {str(exc)}')
return False, None
# add token to form data
form_data['h-captcha-response'] = solved.solution.token
form_data['g-recaptcha-response'] = solved.solution.token
# post form data
response = await session.post(URL, data=form_data)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//div[text()="Verification Success!"]'):
print('The hCaptcha has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The hCaptcha has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_image.py
================================================
# -*- coding: UTF-8 -*-
"""
Image CAPTCHA solving example
"""
import asyncio
import os
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
from unicaps.common import CaptchaCharType, CaptchaAlphabet # type: ignore
URL = 'https://democaptcha.com/demo-form-eng/image.html'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Solve image CAPTCHA """
# make a session and go to URL
async with httpx.AsyncClient(http2=True) as session:
response = await session.get(URL)
# parse page and get captcha URL
page = html.document_fromstring(response.text)
# fetch captcha image
captcha_url = page.cssselect('#htest_image')[0].attrib['src']
captcha_response = await session.get(captcha_url)
# solve the captcha
try:
solved = await solver.solve_image_captcha(
image=captcha_response.content, # binary image data
char_type=CaptchaCharType.ALPHANUMERIC, # consists of alphanumeric characters
is_phrase=False, # not a phrase (no whitespaces)
is_case_sensitive=True, # case-sensitive text
is_math=False, # no calculation needed
alphabet=CaptchaAlphabet.LATIN # latin alphabet being used
)
except exceptions.UnicapsException as exc:
print(f'Image CAPTCHA solving exception: {str(exc)}')
return False, None
form_data = {}
# parse form data
page_form_data = page.xpath('//form//input')
for input_data in page_form_data:
if input_data.xpath('@name'):
form_data[str(input_data.xpath('@name')[0])] = str(
next(iter(input_data.xpath('@value')), None)
)
form_data['message'] = 'test'
# add token to form data
form_data['vericode'] = solved.solution.text
# post form data and parse result page
response = await session.post(URL, data=form_data)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//h2[text()="Your message has been sent (actually not), thank you!"]'):
print('The Image CAPTCHA has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The Image CAPTCHA has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_keycaptcha.py
================================================
"""
KeyCaptcha solving example
"""
import asyncio
import os
import re
from urllib.parse import urljoin
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://2captcha.com/demo/keycaptcha'
URL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/key-captcha/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Get and solve KeyCaptcha """
# create an HTTP2 session
async with httpx.AsyncClient(http2=True) as session:
# open page and extract CAPTCHA URL
response = await session.get(URL)
page = html.document_fromstring(response.text)
# extract captcha URL, parse page and get captcha params
captcha_url = page.xpath('//iframe[@name="key-captcha-widget"]')[0].attrib['src']
captcha_url = urljoin(URL, captcha_url)
response = await session.get(captcha_url)
captcha_page = html.document_fromstring(response.text)
script = captcha_page.xpath('//script[contains(text(), "var s_s_c_user_id")]')[0].text
def extract_var_value(var_name):
return re.search(fr"var {var_name} = '(.+)';", script).group(1)
s_s_c_user_id = extract_var_value('s_s_c_user_id')
s_s_c_session_id = extract_var_value('s_s_c_session_id')
s_s_c_web_server_sign = extract_var_value('s_s_c_web_server_sign')
s_s_c_web_server_sign2 = extract_var_value('s_s_c_web_server_sign2')
# solve KeyCaptcha
try:
solved = await solver.solve_keycaptcha(
page_url=URL,
user_id=s_s_c_user_id,
session_id=s_s_c_session_id,
ws_sign=s_s_c_web_server_sign,
ws_sign2=s_s_c_web_server_sign2
)
except exceptions.UnicapsException as exc:
print(f'KeyCaptcha solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = await session.post(
URL_VERIFY,
json={'capcode': solved.solution.token}
)
# check the result
if response.json().get('success'):
print('The KeyCaptcha has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The KeyCaptcha has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_recaptcha_v2.py
================================================
"""
reCAPTCHA v2 async solving example
"""
import asyncio
import os
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Get and solve reCAPTCHA v2 """
# make a session and go to URL
async with httpx.AsyncClient(http2=True) as session:
response = await session.get(URL)
# parse page and get site-key
page = html.document_fromstring(response.text)
site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']
# parse form data
page_form_data = page.xpath('//form//input')
form_data = {}
for input_data in page_form_data:
form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]
# solve reCAPTCHA
try:
solved = await solver.solve_recaptcha_v2(
site_key=site_key,
page_url=URL
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v2 solving exception: {str(exc)}')
return False, None
# add token to form data
form_data['g-recaptcha-response'] = solved.solution.token
# post form data
response = await session.post(URL, data=form_data)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//h2[text()="Success!"]'):
print('The reCAPTCHA v2 has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The reCAPTCHA v2 has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_recaptcha_v2_enterprise.py
================================================
# -*- coding: UTF-8 -*-
"""
reCAPTCHA v2 Enterprise solving example
"""
import asyncio
import os
import random
import string
import httpx
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
from unicaps.proxy import ProxyServer # type: ignore
URL = 'https://store.steampowered.com/join'
URL_REFRESH_CAPTCHA = 'https://store.steampowered.com/join/refreshcaptcha/'
URL_VERIFY_EMAIL = 'https://store.steampowered.com/join/ajaxverifyemail'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
PROXY = os.getenv(
'HTTP_PROXY_SERVER',
default='http://<LOGIN>:<PASSWORD>@<PROXY_ADDRESS>:<PORT>'
)
def get_random_word(length):
""" Generate a random word of a given length """
letters = string.ascii_lowercase + string.digits
return ''.join(random.choice(letters) for i in range(length))
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Get and solve CAPTCHA """
# make a session, update headers and proxies
async with httpx.AsyncClient(proxies=PROXY) as session:
session.headers.update({
'User-Agent': USER_AGENT,
})
# open the "Join" page just to get session cookies
response = await session.get(URL)
# get reCAPTCHA params
response = await session.post(
URL_REFRESH_CAPTCHA,
data=dict(count=1)
)
captcha_params = response.json()
# solve reCAPTCHA
try:
solved = await solver.solve_recaptcha_v2(
site_key=captcha_params['sitekey'],
page_url=URL,
data_s=captcha_params['s'],
is_enterprise=True,
proxy=ProxyServer(PROXY),
user_agent=USER_AGENT,
cookies=dict(session.cookies)
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v2 Enterprise solving exception: {str(exc)}')
return False, None
# generate email address
email = f'random_{get_random_word(10)}@gmail.com'
# verify email
response = await session.post(
URL_VERIFY_EMAIL,
data=dict(
email=email,
captchagid=captcha_params['gid'],
captcha_text=solved.solution.token,
elang=0
)
)
response_data = response.json()
print(f"Email: {email}\nResult: {response_data['details']}")
# check the result
if 'the CAPTCHA appears to be invalid' not in response_data['details']:
print('The reCAPTCHA v2 Enterprise has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The reCAPTCHA v2 Enterprise has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_recaptcha_v2_invisible.py
================================================
# -*- coding: UTF-8 -*-
"""
reCAPTCHA v2 invisible solving example
"""
import asyncio
import os
import httpx
from lxml import html # type: ignore
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-invisible.php'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Get and solve reCAPTCHA v2 invisible """
# make a session and go to URL
async with httpx.AsyncClient(http2=True) as session:
response = await session.get(URL)
# parse page and get site-key
page = html.document_fromstring(response.text)
site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']
# parse form data
page_form_data = page.xpath('//form//input')
form_data = {}
for input_data in page_form_data:
form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]
# solve reCAPTCHA
try:
solved = await solver.solve_recaptcha_v2(
site_key=site_key,
page_url=URL,
is_invisible=True
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v2 (invisible) solving exception: {str(exc)}')
return False, None
# add token to form data
form_data['g-recaptcha-response'] = solved.solution.token
# post form data
response = await session.post(URL, data=form_data)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//h2[text()="Success!"]'):
print('The Invisible reCAPTCHA v2 has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The Invisible reCAPTCHA v2 has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_recaptcha_v3.py
================================================
# -*- coding: UTF-8 -*-
"""
reCAPTCHA v3 solving example
"""
import asyncio
import os
import re
from pprint import pprint
import httpx
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php'
VERIFY_URL = ('https://recaptcha-demo.appspot.com/recaptcha-v3-verify.php'
'?action={action}&token={token}')
MIN_SCORE = 0.7
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" Get and solve CAPTCHA """
# make a session and go to URL
async with httpx.AsyncClient(http2=True) as session:
response = await session.get(URL)
# extract site-key and action from the page source using regular expression
regexp = re.search(
r"grecaptcha.execute\('([a-zA-Z0-9\-_]{40})', ?\{action: ?'(.*)'\}\)'",
response.text
)
site_key = regexp.group(1)
action = regexp.group(2)
# solve reCAPTCHA
try:
solved = await solver.solve_recaptcha_v3(
site_key=site_key,
page_url=URL,
action=action,
min_score=MIN_SCORE
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v3 solving exception: {str(exc)}')
return False, None
# verify token and print the result
response = await session.get(
VERIFY_URL.format(
action=action,
token=solved.solution.token
)
)
result = response.json()
pprint(result)
# check the result
if not result['score']:
print('The reCAPTCHA v3 has not been solved correctly! Error codes: ' + ', '.join(
result['error-codes']))
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if result['score'] < MIN_SCORE:
print(
f'Solved reCAPTCHA v3 score ({result["score"]}) is less than requested ({MIN_SCORE})!'
)
# report bad CAPTCHA
await solved.report_bad()
return False, solved
print('The reCAPTCHA v3 has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/async_text.py
================================================
"""
TextCaptcha solving example
"""
import asyncio
import os
from unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
from unicaps.common import WorkerLanguage # type: ignore
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
TEXT_CAPTCHA = 'If tomorrow is Sunday, what day is today?'
TEXT_CAPTCHA_ANSWER = 'saturday'
async def main():
""" Init AsyncCaptchaSolver and run the example """
async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:
await run(solver)
async def run(solver):
""" TextCaptcha solving func """
# solve TextCaptcha
try:
solved = await solver.solve_text_captcha(
text=TEXT_CAPTCHA,
language=WorkerLanguage.ENGLISH
)
except exceptions.UnicapsException as exc:
print(f'TextCaptcha solving exception: {str(exc)}')
return False, None
# check the result
if solved.solution.text.lower() == TEXT_CAPTCHA_ANSWER:
print('The TextCaptcha has been solved correctly!')
# report good CAPTCHA
await solved.report_good()
return True, solved
print('The TextCaptcha has not been solved correctly!')
# report bad CAPTCHA
await solved.report_bad()
return False, solved
if __name__ == '__main__':
asyncio.run(main())
================================================
FILE: examples/capy_puzzle.py
================================================
"""
Capy Puzzle CAPTCHA solving example
"""
import os
from urllib.parse import urlparse, parse_qs
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://www.capy.me/products/puzzle_captcha/'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
USER_AGENT = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36')
def run(solver):
""" Solve Capy Puzzle CAPTCHA """
# create an HTTP2 session
with httpx.Client(http2=True, headers={'User-Agent': USER_AGENT}) as session:
# open page and extract CAPTCHA URL
response = session.get(URL)
page = html.document_fromstring(response.text)
capy_url = page.xpath('//script[contains(@src, "/puzzle/get_js/")]')[0].attrib['src']
capy_url_parsed = urlparse(capy_url)
api_server = f'{capy_url_parsed.scheme}://{capy_url_parsed.hostname}'
site_key = parse_qs(capy_url_parsed.query)['k'][0]
# solve Capy Puzzle CAPTCHA
try:
solved = solver.solve_capy_puzzle(
site_key=site_key,
page_url=URL,
api_server=api_server,
user_agent=USER_AGENT
)
except exceptions.UnicapsException as exc:
print(f'Capy Puzzle CAPTCHA solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = session.post(
URL,
data={
'capy_captchakey': solved.solution.captchakey,
'capy_challengekey': solved.solution.challengekey,
'capy_answer': solved.solution.answer
}
)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//div[@class="result success"]'):
print('The Capy Puzzle CAPTCHA has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The Capy Puzzle CAPTCHA has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/funcaptcha.py
================================================
"""
FunCaptcha solving example
"""
import os
import re
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://client-demo.arkoselabs.com/solo-animals'
URL_VERIFY = 'https://client-demo.arkoselabs.com/solo-animals/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Load and solve FunCaptcha """
# create an HTTP2 session
with httpx.Client(http2=True) as session:
# open page and extract CAPTCHA URL
response = session.get(URL)
page = html.document_fromstring(response.text)
# extract Public Key and Service URL values
script = page.xpath('//script[contains(text(), "public_key")]')[0].text
regexp = re.search(
r'public_key: ?"([0-9A-Z-]+)",\s*surl: ?"(.*)",',
script
)
public_key = regexp.group(1)
service_url = regexp.group(2)
# solve FunCaptcha
try:
solved = solver.solve_funcaptcha(
public_key=public_key,
page_url=URL,
service_url=service_url
)
except exceptions.UnicapsException as exc:
print(f'FunCaptcha solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = session.post(
URL_VERIFY,
data={
'name': 'test',
'verification-token': solved.solution.token,
'fc-token': solved.solution.token
}
)
# check the result
if '<h3>Solved!</h3>' in response.text:
print('The FunCaptcha has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The FunCaptcha has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/geetest.py
================================================
"""
GeeTest solving example
"""
import os
import time
import httpx
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://2captcha.com/demo/geetest'
INIT_PARAMS_URL = 'https://2captcha.com/api/v1/captcha-demo/gee-test/init-params?t={ms}'
URL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Solve GeeTest """
# create an HTTP2 session
with httpx.Client(http2=True) as session:
# open page and extract CAPTCHA URL
response = session.get(INIT_PARAMS_URL.format(ms=int(time.time() * 1000)))
init_params = response.json()
# solve GeeTest
try:
solved = solver.solve_geetest(
page_url=URL,
gt_key=init_params['gt'],
challenge=init_params['challenge']
)
except exceptions.UnicapsException as exc:
print(f'GeeTest solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = session.post(
URL_VERIFY,
json={
'geetest_challenge': solved.solution.challenge,
'geetest_seccode': solved.solution.seccode,
'geetest_validate': solved.solution.validate
}
)
# check the result
if response.json().get('success'):
print('The GeeTest has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The GeeTest has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/geetest_v4.py
================================================
"""
GeeTest solving example
"""
import os
import re
from urllib.parse import urljoin
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://2captcha.com/demo/geetest-v4'
URL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test-v4/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Solve GeeTest v4 """
# create an HTTP2 session
with httpx.Client(http2=True) as session:
# open page and extract pages-CaptchaDemo URL
response = session.get(URL)
page = html.document_fromstring(response.text)
captcha_js_url = page.xpath(
'//script[@data-chunk="pages-CaptchaDemo" and '
'starts-with(@src, "/dist/web/pages-CaptchaDemo.")]'
)[0].attrib['src']
captcha_js_url = urljoin(URL, captcha_js_url)
# load pages-CaptchaDemo js-file and extract captcha ID
response = session.get(captcha_js_url)
regexp = re.search(
r'window\.initGeetest4\(\{\s*captchaId:\s?"([0-9a-z]+)"',
response.text
)
# solve GeeTest v4
try:
solved = solver.solve_geetest_v4(
page_url=URL,
captcha_id=regexp.group(1)
)
except exceptions.UnicapsException as exc:
print(f'GeeTestV4 solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = session.post(
URL_VERIFY,
json={
'captcha_id': solved.solution.captcha_id,
'lot_number': solved.solution.lot_number,
'pass_token': solved.solution.pass_token,
'gen_time': solved.solution.gen_time,
'captcha_output': solved.solution.captcha_output
}
)
# check the result
if response.json().get('result') == 'success':
print('The GeeTest v4 has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The GeeTest v4 has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/hcaptcha.py
================================================
# -*- coding: UTF-8 -*-
"""
hCaptcha solving example
"""
import os
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://accounts.hcaptcha.com/demo'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Solve hCaptcha """
# make a session and go to URL
with httpx.Client(http2=True) as session:
response = session.get(URL)
# parse page and get site-key
page = html.document_fromstring(response.text)
site_key = page.cssselect('.h-captcha')[0].attrib['data-sitekey']
# parse form data
page_form_data = page.xpath('//form//input')
form_data = {}
for input_data in page_form_data:
if input_data.xpath('@name'):
form_data[input_data.xpath('@name')[0]] = next(
iter(input_data.xpath('@value')),
None
)
# solve hCaptcha
try:
solved = solver.solve_hcaptcha(
site_key=site_key,
page_url=URL
)
except exceptions.UnicapsException as exc:
print(f'hCaptcha solving exception: {str(exc)}')
return False, None
# add token to form data
form_data['h-captcha-response'] = solved.solution.token
form_data['g-recaptcha-response'] = solved.solution.token
# post form data
response = session.post(URL, data=form_data)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//div[text()="Verification Success!"]'):
print('The hCaptcha has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The hCaptcha has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/image.py
================================================
# -*- coding: UTF-8 -*-
"""
Image CAPTCHA solving example
"""
import os
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
from unicaps.common import CaptchaCharType, CaptchaAlphabet # type: ignore
URL = 'https://democaptcha.com/demo-form-eng/image.html'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Solve image CAPTCHA """
# make a session and go to URL
with httpx.Client(http2=True) as session:
response = session.get(URL)
response.raise_for_status()
# parse page and get captcha URL
page = html.document_fromstring(response.text)
# fetch captcha image
captcha_url = page.cssselect('#htest_image')[0].attrib['src']
captcha_response = session.get(captcha_url)
captcha_response.raise_for_status()
# solve the captcha
try:
solved = solver.solve_image_captcha(
image=captcha_response.content, # binary image data
char_type=CaptchaCharType.ALPHANUMERIC, # consists of alphanumeric characters
is_phrase=False, # not a phrase (no whitespaces)
is_case_sensitive=True, # case-sensitive text
is_math=False, # no calculation needed
alphabet=CaptchaAlphabet.LATIN # latin alphabet being used
)
except exceptions.UnicapsException as exc:
print(f'Image CAPTCHA solving exception: {str(exc)}')
return False, None
form_data = {}
# parse form data
page_form_data = page.xpath('//form//input')
for input_data in page_form_data:
if input_data.xpath('@name'):
form_data[str(input_data.xpath('@name')[0])] = str(
next(iter(input_data.xpath('@value')), None)
)
form_data['message'] = 'test'
# add token to form data
form_data['vericode'] = solved.solution.text
# post form data and parse result page
response = session.post(URL, data=form_data)
response.raise_for_status()
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//h2[text()="Your message has been sent (actually not), thank you!"]'):
print('The Image CAPTCHA has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The Image CAPTCHA has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/keycaptcha.py
================================================
"""
KeyCaptcha solving example
"""
import os
import re
from urllib.parse import urljoin
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://2captcha.com/demo/keycaptcha'
URL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/key-captcha/verify'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Get and solve KeyCaptcha """
# create an HTTP2 session
with httpx.Client(http2=True) as session:
# open page and extract CAPTCHA URL
response = session.get(URL)
page = html.document_fromstring(response.text)
# extract captcha URL, parse page and get captcha params
captcha_url = page.xpath('//iframe[@name="key-captcha-widget"]')[0].attrib['src']
captcha_url = urljoin(URL, captcha_url)
response = session.get(captcha_url)
captcha_page = html.document_fromstring(response.text)
script = captcha_page.xpath('//script[contains(text(), "var s_s_c_user_id")]')[0].text
def extract_var_value(var_name):
return re.search(fr"var {var_name} = '(.+)';", script).group(1)
s_s_c_user_id = extract_var_value('s_s_c_user_id')
s_s_c_session_id = extract_var_value('s_s_c_session_id')
s_s_c_web_server_sign = extract_var_value('s_s_c_web_server_sign')
s_s_c_web_server_sign2 = extract_var_value('s_s_c_web_server_sign2')
# solve KeyCaptcha
try:
solved = solver.solve_keycaptcha(
page_url=URL,
user_id=s_s_c_user_id,
session_id=s_s_c_session_id,
ws_sign=s_s_c_web_server_sign,
ws_sign2=s_s_c_web_server_sign2
)
except exceptions.UnicapsException as exc:
print(f'KeyCaptcha solving exception: {str(exc)}')
return False, None
# post solved captcha token
response = session.post(
URL_VERIFY,
json={'capcode': solved.solution.token}
)
# check the result
if response.json().get('success'):
print('The KeyCaptcha has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The KeyCaptcha has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/recaptcha_v2.py
================================================
# -*- coding: UTF-8 -*-
"""
reCAPTCHA v2 solving example
"""
import os
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Get and solve reCAPTCHA v2 """
# make a session and go to URL
with httpx.Client(http2=True) as session:
response = session.get(URL)
# parse page and get data-sitekey value
page = html.document_fromstring(response.text)
site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']
# parse form data
page_form_data = page.xpath('//form//input')
form_data = {}
for input_data in page_form_data:
form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]
# solve reCAPTCHA
try:
solved = solver.solve_recaptcha_v2(
site_key=site_key,
page_url=URL
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v2 solving exception: {str(exc)}')
return False, None
# add token to form data
form_data['g-recaptcha-response'] = solved.solution.token
# post form data
response = session.post(URL, data=form_data)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//h2[text()="Success!"]'):
print('The reCAPTCHA v2 has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The reCAPTCHA v2 has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/recaptcha_v2_enterprise.py
================================================
# -*- coding: UTF-8 -*-
"""
reCAPTCHA v2 Enterprise solving example
"""
import os
import random
import string
from urllib.parse import urlparse
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
from unicaps.proxy import ProxyServer # type: ignore
URL = 'https://store.steampowered.com/join'
URL_REFRESH_CAPTCHA = 'https://store.steampowered.com/join/refreshcaptcha/'
URL_VERIFY_EMAIL = 'https://store.steampowered.com/join/ajaxverifyemail'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'
PROXY = os.getenv(
'HTTP_PROXY_SERVER',
default='http://<LOGIN>:<PASSWORD>@<PROXY_ADDRESS>:<PORT>'
)
def get_random_word(length):
""" Generate a random word of a given length """
letters = string.ascii_lowercase + string.digits
return ''.join(random.choice(letters) for i in range(length))
def run(solver):
""" Get and solve CAPTCHA """
# make a session, update headers and proxies
with httpx.Client(proxies=PROXY) as session:
session.headers.update({
'User-Agent': USER_AGENT,
})
# open the "Join" page
response = session.get(URL)
response.raise_for_status()
# parse the page, then find and extract reCAPTCHA domain
page = html.document_fromstring(response.text)
recaptcha_url = page.xpath(
'//script[contains(@src, "recaptcha/enterprise.js")]')[0].attrib['src']
recaptcha_domain = urlparse(recaptcha_url).netloc
# get reCAPTCHA params
captcha_params = session.post(
URL_REFRESH_CAPTCHA,
data=dict(count=1)
).json()
# solve reCAPTCHA
try:
solved = solver.solve_recaptcha_v2(
site_key=captcha_params['sitekey'],
page_url=URL,
is_enterprise=True,
data_s=captcha_params['s'],
api_domain=recaptcha_domain,
proxy=ProxyServer(PROXY),
user_agent=USER_AGENT
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v2 Enterprise solving exception: {str(exc)}')
return False, None
# generate email address
email = f'random_{get_random_word(10)}@gmail.com'
# verify email
response = session.post(
URL_VERIFY_EMAIL,
data=dict(
email=email,
captchagid=captcha_params['gid'],
captcha_text=solved.solution.token,
elang=0
)
)
response.raise_for_status()
response_data = response.json()
print(f"Email: {email}\nResult: {response_data['details']}")
# check the result
if 'the CAPTCHA appears to be invalid' not in response_data['details']:
print('The reCAPTCHA v2 Enterprise has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The reCAPTCHA v2 Enterprise has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/recaptcha_v2_invisible.py
================================================
# -*- coding: UTF-8 -*-
"""
reCAPTCHA v2 invisible solving example
"""
import os
import httpx
from lxml import html # type: ignore
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-invisible.php'
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Get and solve reCAPTCHA v2 invisible """
# make a session and go to URL
with httpx.Client(http2=True) as session:
response = session.get(URL)
# parse page and get site-key
page = html.document_fromstring(response.text)
site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']
# parse form data
page_form_data = page.xpath('//form//input')
form_data = {}
for input_data in page_form_data:
form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]
# solve reCAPTCHA
try:
solved = solver.solve_recaptcha_v2(
site_key=site_key,
page_url=URL,
is_invisible=True
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v2 (invisible) solving exception: {str(exc)}')
return False, None
# add token to form data
form_data['g-recaptcha-response'] = solved.solution.token
# post form data
response = session.post(URL, data=form_data)
page = html.document_fromstring(response.text)
# check the result
if page.xpath('//h2[text()="Success!"]'):
print('The Invisible reCAPTCHA v2 has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The Invisible reCAPTCHA v2 has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/recaptcha_v3.py
================================================
# -*- coding: UTF-8 -*-
"""
reCAPTCHA v3 solving example
"""
import os
import re
from pprint import pprint
import httpx
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
URL = 'https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php'
VERIFY_URL = ('https://recaptcha-demo.appspot.com/recaptcha-v3-verify.php'
'?action={action}&token={token}')
MIN_SCORE = 0.7
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
def run(solver):
""" Get and solve CAPTCHA """
# make a session and go to URL
with httpx.Client(http2=True) as session:
response = session.get(URL)
# extract site-key and action from the page source using regular expression
regexp = re.search(
r"grecaptcha.execute\('([a-zA-Z0-9\-_]{40})', ?\{action: ?'(.*)'\}\)'",
response.text
)
site_key = regexp.group(1)
action = regexp.group(2)
# solve reCAPTCHA
try:
solved = solver.solve_recaptcha_v3(
site_key=site_key,
page_url=URL,
action=action,
min_score=MIN_SCORE
)
except exceptions.UnicapsException as exc:
print(f'reCAPTCHA v3 solving exception: {str(exc)}')
return False, None
# verify token and print the result
response = session.get(
VERIFY_URL.format(
action=action,
token=solved.solution.token
)
)
result = response.json()
pprint(result)
# check the result
if not result['score']:
print(
'The reCAPTCHA v3 has not been solved correctly! Error codes: ' + ', '.join(
result['error-codes'])
)
# report bad CAPTCHA
solved.report_bad()
return False, solved
if result['score'] < MIN_SCORE:
print(
f'Solved reCAPTCHA v3 score ({result["score"]}) is less than requested ({MIN_SCORE})!'
)
# report bad CAPTCHA
solved.report_bad()
return False, solved
print('The reCAPTCHA v3 has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: examples/requirements.txt
================================================
httpx[http2]
lxml
================================================
FILE: examples/run_all.py
================================================
# -*- coding: UTF-8 -*-
"""
All examples runner
"""
from importlib import import_module
import logging
import os
from unicaps import CaptchaSolver # type: ignore
# services dict: key is a name of CAPTCHA solving service, value is an env variable containing
# the API key
SERVICES = {
'2captcha.com': 'API_KEY_2CAPTCHA',
'anti-captcha.com': 'API_KEY_ANTICAPTCHA',
'azcaptcha.com': 'API_KEY_AZCAPTCHA',
'captcha.guru': 'API_KEY_CAPTCHA_GURU',
'cptch.net': 'API_KEY_CPTCH_NET'
}
# list of modules containing CAPTCHA solving examples
EXAMPLES = [
'image',
'recaptcha_v2',
'recaptcha_v2_invisible',
'recaptcha_v2_enterprise',
'recaptcha_v3',
'hcaptcha',
'keycaptcha',
'geetest',
'geetest_v4',
'capy_puzzle',
'text',
'funcaptcha'
]
logging.basicConfig(level=logging.DEBUG)
if __name__ == '__main__':
for service_name, env_var_name in SERVICES.items():
api_key = os.getenv(env_var_name)
print(f'######### Service: {service_name} #########')
# init captcha solver
with CaptchaSolver(service_name, api_key) as solver:
for example_name in EXAMPLES:
print(example_name)
module = import_module(example_name)
module.run(solver)
print()
================================================
FILE: examples/text.py
================================================
"""
TextCaptcha solving example
"""
import os
from unicaps import CaptchaSolver, CaptchaSolvingService, exceptions # type: ignore
from unicaps.common import WorkerLanguage # type: ignore
API_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')
TEXT_CAPTCHA = 'If tomorrow is Sunday, what day is today?'
TEXT_CAPTCHA_ANSWER = 'saturday'
def run(solver):
""" Solve TextCaptcha """
# solve TextCaptcha
try:
solved = solver.solve_text_captcha(
text=TEXT_CAPTCHA,
language=WorkerLanguage.ENGLISH
)
except exceptions.UnicapsException as exc:
print(f'TextCaptcha solving exception: {str(exc)}')
return False, None
# check the result
if solved.solution.text.lower() == TEXT_CAPTCHA_ANSWER:
print('The TextCaptcha has been solved correctly!')
# report good CAPTCHA
solved.report_good()
return True, solved
print('The TextCaptcha has not been solved correctly!')
# report bad CAPTCHA
solved.report_bad()
return False, solved
if __name__ == '__main__':
with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:
run(captcha_solver)
================================================
FILE: requirements-dev.txt
================================================
flake8>=3.8.3
pytest>=6.0.1
tox>=3.17.1
================================================
FILE: requirements.txt
================================================
httpx>=0.22.0
enforce-typing
================================================
FILE: setup.cfg
================================================
[pycodestyle]
ignore = E701
max-line-length = 100
[pylint.checkers]
max-line-length = 100
max-args = 8
max-attributes = 12
[pylint.messages_control]
disable = E0402,R0903,W0236
================================================
FILE: setup.py
================================================
import setuptools
with open("README.md", "r", encoding="utf-8") as fh:
long_description = fh.read()
setuptools.setup(
name="unicaps",
version="1.3.0",
author="Sergey Scat",
author_email="py.unicaps@gmail.com",
description="Universal CAPTCHA Solver for humans",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://github.com/sergey-scat/unicaps",
packages=setuptools.find_packages(),
install_requires=["httpx>=0.22.0", "enforce-typing>=1.0.0"],
classifiers=[
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11"
],
python_requires='>=3.7',
)
================================================
FILE: tests/_helpers.py
================================================
"""
Helpers
"""
import collections.abc
def dict_update(src_dict, upd_dict):
""" Updates nested dict recursively """
for key, value in upd_dict.items():
if isinstance(value, collections.abc.Mapping):
src_dict[key] = dict_update(src_dict.get(key, {}), value)
else:
src_dict[key] = value
return src_dict
================================================
FILE: tests/conftest.py
================================================
# -*- coding: UTF-8 -*-
import enum
import importlib
import os.path
import random
import string
import sys
from pytest import fixture
cd = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(1, cd)
from unicaps._captcha import CaptchaType
@fixture()
def services():
pass
@fixture()
def captcha_service():
pass
def _gen_random_word(length):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(length))
def _get_random_value(field):
cls = getattr(field.type, '__args__', [field.type])[0]
if cls is str:
return _gen_random_word(10)
if cls is bytes:
return _gen_random_word(32).encode('utf-8')
if cls is int:
return random.randint(1, 255)
if cls is bool:
return bool(random.randint(0, 1))
if issubclass(cls, enum.Enum):
return getattr(cls, cls._member_names_[0])
return cls()
@fixture(scope="module", params=CaptchaType)
def captcha_class(request):
captcha_package = importlib.import_module('unicaps.captcha')
return getattr(captcha_package, request.param.value)
@fixture(scope="module")
def captcha_instance(captcha_class):
# return random_dataclass_init(captcha_class)
params = captcha_class.__dataclass_fields__.copy()
for name, field in params.items():
params[name] = _get_random_value(field)
# for ImageCaptcha
if name == 'image':
params[name] = params[name][:6] + b'Exif' + params[name][10:]
return captcha_class(**params)
================================================
FILE: tests/data/__init__.py
================================================
================================================
FILE: tests/data/data.py
================================================
# -*- coding: UTF-8 -*-
"""
Test data
"""
import base64
import json
import os.path
import pathlib
from random import choice
from unittest import mock
from unicaps import CaptchaSolvingService, exceptions as exc # type: ignore
from unicaps.captcha import ( # type: ignore
CaptchaType, ImageCaptcha, RecaptchaV2, RecaptchaV3, FunCaptcha, TextCaptcha,
KeyCaptcha, GeeTest, GeeTestV4, HCaptcha, CapyPuzzle, TikTokCaptcha
)
from unicaps.common import CaptchaAlphabet, CaptchaCharType, WorkerLanguage # type: ignore
from unicaps.proxy import ProxyServer # type: ignore
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
API_KEY = 'test'
IMAGE_FILE_PATH = os.path.join(CURRENT_DIR, 'image.jpg')
IMAGE_FILE_PATHLIB = pathlib.Path(IMAGE_FILE_PATH)
IMAGE_FILE_FILEOBJECT = open(IMAGE_FILE_PATH, 'rb')
with open(IMAGE_FILE_PATH, 'rb') as f:
IMAGE_FILE_BYTES = f.read()
IMAGE_FILE_BASE64 = base64.b64encode(IMAGE_FILE_BYTES)
IMAGE_FILE_BASE64_STR = IMAGE_FILE_BASE64.decode('ascii')
PROXY_ADDRESS = 'http://login:password@example.com:8080'
PROXY_OBJ = ProxyServer(PROXY_ADDRESS)
PROXY_TYPE = 'HTTP'
COOKIES = {'cookie1': 'value1', 'cookie2': 'value2'}
INPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC = {
1: (ImageCaptcha(IMAGE_FILE_BYTES), None, None, None),
2: (ImageCaptcha(IMAGE_FILE_PATHLIB), None, None, None),
3: (ImageCaptcha(IMAGE_FILE_FILEOBJECT, is_phrase=True), None, None, None),
4: (ImageCaptcha(IMAGE_FILE_BYTES, is_case_sensitive=True), None, None, None),
5: (ImageCaptcha(IMAGE_FILE_BYTES, char_type=CaptchaCharType.ALPHA), None, None, None),
6: (ImageCaptcha(IMAGE_FILE_BYTES, is_math=True), None, None, None),
7: (ImageCaptcha(IMAGE_FILE_BYTES, min_len=1), None, None, None),
8: (ImageCaptcha(IMAGE_FILE_BYTES, max_len=10), None, None, None),
9: (ImageCaptcha(IMAGE_FILE_BYTES, alphabet=CaptchaAlphabet.LATIN), None, None, None),
10: (ImageCaptcha(IMAGE_FILE_BYTES, language=WorkerLanguage.ENGLISH), None, None, None),
11: (ImageCaptcha(IMAGE_FILE_BYTES, comment='test'), None, None, None),
12: (RecaptchaV2('test1', 'test2'), None, None, None),
13: (RecaptchaV2('test1', 'test2', is_invisible=True), None, None, None),
14: (RecaptchaV2('test1', 'test2', data_s='test3'), None, None, None),
15: (RecaptchaV3('test1', 'test2'), None, None, None),
16: (RecaptchaV3('test1', 'test2', action='test3'), None, None, None),
17: (RecaptchaV3('test1', 'test2', min_score=0.9), None, None, None),
18: (FunCaptcha('test1', 'test2'), None, None, None),
19: (FunCaptcha('test1', 'test2', service_url='test3'), None, None, None),
20: (FunCaptcha('test1', 'test2', no_js=True), None, None, None),
21: (TextCaptcha('test1'), None, None, None),
22: (TextCaptcha('test1', alphabet=CaptchaAlphabet.LATIN), None, None, None),
23: (TextCaptcha('test1', language=WorkerLanguage.ENGLISH), None, None, None),
24: (KeyCaptcha('test1', 'test2', 'test3', 'test4', 'test5'), None, None, None),
25: (GeeTest('test1', 'test2', 'test3'), None, None, None),
26: (GeeTest('test1', 'test2', 'test3', api_server='test4'), None, None, None),
27: (HCaptcha('test1', 'test2'), None, None, None),
28: (CapyPuzzle('test1', 'test2', 'test3'), None, None, None),
29: (TikTokCaptcha('test1'), None, None, COOKIES),
30: (RecaptchaV2('test1', 'test2'), PROXY_OBJ, None, None),
31: (RecaptchaV2('test1', 'test2'), PROXY_OBJ, 'User-Agent1', None),
32: (RecaptchaV2('test1', 'test2'), PROXY_OBJ, 'User-Agent1', COOKIES),
33: (RecaptchaV2('test1', 'test2', data_s="test3", is_enterprise=True), None, None, None),
34: (RecaptchaV3('test1', 'test2', is_enterprise=True), None, None, None),
35: (TikTokCaptcha('test1', aid=1459), None, None, COOKIES),
36: (TikTokCaptcha('test1', host='test2'), None, None, COOKIES),
37: (TikTokCaptcha('https://www.tiktok.com/login/phone-or-email/email'), None, None, COOKIES),
38: (TikTokCaptcha('https://ads.tiktok.com/i18n/signup'), None, None, COOKIES),
39: (GeeTestV4('test1', 'test2'), None, None, None),
40: (RecaptchaV2('test1', 'test2', api_domain='recaptcha.net'), None, None, None),
41: (RecaptchaV3('test1', 'test2', api_domain='recaptcha.net'), None, None, None),
42: (FunCaptcha('test1', 'test2', blob='test3'), None, None, None),
}
BASE_TASK_REQUEST_DATA = {
CaptchaSolvingService.TWOCAPTCHA: dict(
method='POST',
url='https://2captcha.com/in.php',
headers={'Accept': 'application/json'},
data=dict(key=API_KEY, json=1, soft_id=2738)
),
CaptchaSolvingService.RUCAPTCHA: dict(
method='POST',
url='https://rucaptcha.com/in.php',
headers={'Accept': 'application/json'},
data=dict(key=API_KEY, json=1, soft_id=2738)
),
CaptchaSolvingService.ANTI_CAPTCHA: dict(
method='POST',
json=dict(clientKey=API_KEY, softId=940),
headers={'Accept': 'application/json'},
url='https://api.anti-captcha.com/createTask'
),
CaptchaSolvingService.AZCAPTCHA: dict(
method='POST',
url='http://azcaptcha.com/in.php',
data=dict(key=API_KEY, json=1)
),
CaptchaSolvingService.CPTCH_NET: dict(
method='POST',
url='https://cptch.net/in.php',
data=dict(key=API_KEY, json=1, soft_id="164")
),
CaptchaSolvingService.DEATHBYCAPTCHA: dict(
method='POST',
headers={'Accept': 'application/json'},
url='http://api.dbcapi.me/api/captcha',
data=dict(authtoken=API_KEY)
)
}
OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC = {
CaptchaSolvingService.TWOCAPTCHA: {
1: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},
2: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},
3: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, phrase=1)},
4: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, regsense=1)},
5: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, numeric=2)},
6: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, calc=1)},
7: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, min_len=1)},
8: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, max_len=10)},
9: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, language=2)},
10: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, lang='en')},
11: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, textinstructions='test')},
12: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},
13: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=1)},
14: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',
'invisible': 0, 'data-s': 'test3'}},
15: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',
pageurl='test2')},
16: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
action='test3')},
17: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
min_score=0.9)},
18: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2')},
19: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2', surl='test3')},
20: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2', nojs=1)},
21: {'data': dict(textcaptcha='test1')},
22: {'data': dict(textcaptcha='test1', language=2)},
23: {'data': dict(textcaptcha='test1', lang='en')},
24: {'data': dict(method='keycaptcha', pageurl='test1', s_s_c_user_id='test2',
s_s_c_session_id='test3', s_s_c_web_server_sign='test4',
s_s_c_web_server_sign2='test5')},
25: {'data': dict(method='geetest', pageurl='test1', gt='test2', challenge='test3')},
26: {'data': dict(method='geetest', pageurl='test1', gt='test2', challenge='test3',
api_server='test4')},
27: {'data': dict(method='hcaptcha', sitekey='test1', pageurl='test2', invisible=0)},
28: {'data': dict(method='capy', captchakey='test1', pageurl='test2', api_server='test3')},
29: {'data': dict(method='tiktok', pageurl='test1', aid=None, host=None,
cookies='cookie1:value1;cookie2:value2')},
30: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,
proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],
proxytype=PROXY_TYPE)},
31: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,
proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],
proxytype=PROXY_TYPE,
userAgent='User-Agent1')},
32: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,
proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],
proxytype=PROXY_TYPE,
userAgent='User-Agent1',
cookies='cookie1:value1;cookie2:value2')},
33: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',
'invisible': 0, 'data-s': 'test3', 'enterprise': 1}},
34: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
enterprise=1)},
35: {'data': dict(method='tiktok', pageurl='test1', aid=1459, host=None,
cookies='cookie1:value1;cookie2:value2')},
36: {'data': dict(method='tiktok', pageurl='test1', aid=None, host='test2',
cookies='cookie1:value1;cookie2:value2')},
37: {'data': dict(method='tiktok',
pageurl='https://www.tiktok.com/login/phone-or-email/email',
aid=1459, host='https://www-useast1a.tiktok.com',
cookies='cookie1:value1;cookie2:value2')},
38: {'data': dict(method='tiktok', pageurl='https://ads.tiktok.com/i18n/signup',
aid=1583, host='https://verify-sg.byteoversea.com',
cookies='cookie1:value1;cookie2:value2')},
39: {'data': dict(method='geetest_v4 ', pageurl='test1', captcha_id='test2')},
40: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2',
invisible=0, domain='recaptcha.net')},
41: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
domain='recaptcha.net')},
42: {'data': {'method': 'funcaptcha', 'publickey': 'test1', 'pageurl': 'test2',
'data[blob]': 'test3'}},
},
CaptchaSolvingService.ANTI_CAPTCHA: {
1: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR))},
2: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR))},
3: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,
phrase=True))},
4: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR, case=True))},
5: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR, numeric=2))},
6: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR, math=True))},
7: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,
minLength=1))},
8: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,
maxLength=10))},
9: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR))},
10: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR),
languagePool='en')},
11: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,
comment='test'))},
12: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',
websiteURL='test2', isInvisible=False))},
13: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',
websiteURL='test2', isInvisible=True))},
14: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',
websiteURL='test2', isInvisible=False,
recaptchaDataSValue='test3'))},
15: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',
websiteURL='test2'))},
16: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',
websiteURL='test2', pageAction='test3'))},
17: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',
websiteURL='test2', minScore=0.9))},
18: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',
websiteURL='test2'))},
19: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',
websiteURL='test2', funcaptchaApiJSSubdomain='test3'))},
20: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',
websiteURL='test2'))},
21: None,
22: None,
23: None,
24: None,
25: {'json': dict(task=dict(type='GeeTestTaskProxyless', websiteURL='test1',
gt='test2', challenge='test3'))},
26: {'json': dict(task=dict(type='GeeTestTaskProxyless', websiteURL='test1',
gt='test2', challenge='test3',
geetestApiServerSubdomain='test4'))},
27: {'json': dict(task=dict(type='HCaptchaTaskProxyless', websiteKey='test1',
websiteURL='test2', isInvisible=False))},
28: None,
29: None,
30: {'json': dict(task=dict(type='NoCaptchaTask', websiteKey='test1',
websiteURL='test2', isInvisible=False,
proxyType=PROXY_OBJ.proxy_type.value,
proxyAddress=PROXY_OBJ.get_ip_address(),
proxyPort=PROXY_OBJ.port,
proxyLogin=PROXY_OBJ.login,
proxyPassword=PROXY_OBJ.password))},
31: {'json': dict(task=dict(type='NoCaptchaTask', websiteKey='test1',
websiteURL='test2', isInvisible=False,
proxyType=PROXY_OBJ.proxy_type.value,
proxyAddress=PROXY_OBJ.get_ip_address(),
proxyPort=PROXY_OBJ.port,
proxyLogin=PROXY_OBJ.login,
proxyPassword=PROXY_OBJ.password,
userAgent='User-Agent1'))},
32: {'json': dict(task=dict(type='NoCaptchaTask', websiteKey='test1',
websiteURL='test2', isInvisible=False,
proxyType=PROXY_OBJ.proxy_type.value,
proxyAddress=PROXY_OBJ.get_ip_address(),
proxyPort=PROXY_OBJ.port,
proxyLogin=PROXY_OBJ.login,
proxyPassword=PROXY_OBJ.password,
userAgent='User-Agent1',
cookies='cookie1=value1; cookie2=value2'))},
33: {'json': dict(task=dict(type='RecaptchaV2EnterpriseTaskProxyless', websiteKey='test1',
websiteURL='test2', isInvisible=False,
enterprisePayload=dict(s='test3')))},
34: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',
websiteURL='test2', isEnterprise=True))},
35: None,
36: None,
37: None,
38: None,
39: {'json': dict(task=dict(type='GeeTestTaskProxyless', websiteURL='test1',
gt='test2', version=4))},
40: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',
websiteURL='test2', isInvisible=False,
apiDomain='recaptcha.net'))},
41: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',
websiteURL='test2', apiDomain='recaptcha.net'))},
42: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',
websiteURL='test2', data='{"blob": "test3"}'))},
},
CaptchaSolvingService.AZCAPTCHA: {
1: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},
2: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},
3: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, phrase=1)},
4: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, regsense=1)},
5: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, numeric=2)},
6: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, calc=1)},
7: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, min_len=1)},
8: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, max_len=10)},
9: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, language=2)},
10: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, lang='en')},
11: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, textinstructions='test')},
12: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},
13: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=1)},
14: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',
'invisible': 0, 'data-s': 'test3'}},
15: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',
pageurl='test2')},
16: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
action='test3')},
17: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
min_score=0.9)},
18: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2')},
19: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2', surl='test3')},
20: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2')},
21: None,
22: None,
23: None,
24: None,
25: None,
26: None,
27: {'data': dict(method='hcaptcha', sitekey='test1', pageurl='test2')},
28: None,
29: None,
30: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,
proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],
proxytype=PROXY_TYPE)},
31: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,
proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],
proxytype=PROXY_TYPE,
userAgent='User-Agent1')},
32: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,
proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],
proxytype=PROXY_TYPE,
userAgent='User-Agent1',
cookies='cookie1:value1;cookie2:value2')},
33: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',
'invisible': 0, 'data-s': 'test3'}},
34: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',
pageurl='test2')},
35: None,
36: None,
37: None,
38: None,
39: None,
40: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',
'invisible': 0}},
41: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',
pageurl='test2')},
42: {'data': {'method': 'funcaptcha', 'publickey': 'test1', 'pageurl': 'test2',
'data[blob]': 'test3'}},
},
CaptchaSolvingService.CPTCH_NET: {
1: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},
2: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},
3: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, phrase=1)},
4: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, regsense=1)},
5: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, numeric=2)},
6: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, calc=1)},
7: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, min_len=1)},
8: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, max_len=10)},
9: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, language=2)},
10: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, lang='en')},
11: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, textinstructions='test')},
12: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},
13: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=1)},
14: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',
'invisible': 0}},
15: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',
pageurl='test2')},
16: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
action='test3')},
17: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',
min_score=0.9)},
18: None,
19: None,
20: None,
21: None,
22: None,
23: None,
24: None,
25: None,
26: None,
27: None,
28: None,
29: None,
30: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},
31: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},
32: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},
33: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},
34: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',
pageurl='test2')},
35: None,
36: None,
37: None,
38: None,
39: None,
40: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',
'invisible': 0}},
41: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',
pageurl='test2')},
42: None,
},
CaptchaSolvingService.DEATHBYCAPTCHA: {
1: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
2: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
3: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
4: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
5: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
6: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
7: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
8: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
9: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
10: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
11: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},
12: {'data': dict(type=4,
token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2'}))},
13: {'data': dict(type=4,
token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2'}))},
14: {'data': dict(type=4, token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2',
'data-s': 'test3'}))},
15: {'data': dict(type=5,
token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2'}))},
16: {'data': dict(type=5, token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2',
'action': 'test3'}))},
17: {'data': dict(type=5, token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2',
'min_score': 0.9}))},
18: {'data': dict(type=6,
funcaptcha_params=json.dumps({'publickey': 'test1',
'pageurl': 'test2'}))},
19: {'data': dict(type=6,
funcaptcha_params=json.dumps({'publickey': 'test1',
'pageurl': 'test2'}))},
20: {'data': dict(type=6,
funcaptcha_params=json.dumps({'publickey': 'test1',
'pageurl': 'test2'}))},
21: None,
22: None,
23: None,
24: None,
25: None,
26: None,
27: {'data': dict(type=7,
hcaptcha_params=json.dumps({'sitekey': 'test1', 'pageurl': 'test2'}))},
28: None,
29: None,
30: {'data': dict(
type=4,
token_params=json.dumps(
dict(
googlekey='test1',
pageurl='test2',
proxy=PROXY_ADDRESS,
proxytype=PROXY_TYPE
)
)
)},
31: {'data': dict(
type=4,
token_params=json.dumps(
dict(
googlekey='test1',
pageurl='test2',
proxy=PROXY_ADDRESS,
proxytype=PROXY_TYPE
)
)
)},
32: {'data': dict(
type=4,
token_params=json.dumps(
dict(
googlekey='test1',
pageurl='test2',
proxy=PROXY_ADDRESS,
proxytype=PROXY_TYPE
)
)
)},
33: {'data': dict(
type=4,
token_params=json.dumps(
{'googlekey': 'test1', 'pageurl': 'test2', 'data-s': 'test3'}
)
)},
34: {'data': dict(
type=5,
token_params=json.dumps(
dict(googlekey='test1', pageurl='test2')
)
)},
35: None,
36: None,
37: None,
38: None,
39: None,
40: {'data': dict(
type=4,
token_params=json.dumps(
dict(googlekey='test1', pageurl='test2')
)
)},
41: {'data': dict(
type=5,
token_params=json.dumps(
dict(googlekey='test1', pageurl='test2')
)
)},
42: {'data': dict(
type=6,
funcaptcha_params=json.dumps(
dict(publickey='test1', pageurl='test2')
)
)},
}
}
OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC[CaptchaSolvingService.RUCAPTCHA] = (
OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC[CaptchaSolvingService.TWOCAPTCHA]
)
def get_http_resp_obj(ret_value, status_code=200, reason_phrase='OK', is_success=True,
is_error=False):
""" Mocked response object """
obj = mock.Mock()
obj.json = ret_value.copy
obj.status_code = status_code
obj.reason_phrase = reason_phrase
obj.is_success = is_success
obj.is_error = is_error
return obj
INPUT_TEST_LIST_FOR_TASK_PARSE_RESPONSE_FUNC = {
1: CaptchaType.IMAGE,
2: CaptchaType.RECAPTCHAV2,
3: CaptchaType.RECAPTCHAV3,
4: CaptchaType.TEXT,
5: CaptchaType.FUNCAPTCHA,
6: CaptchaType.KEYCAPTCHA,
7: CaptchaType.GEETEST,
8: CaptchaType.HCAPTCHA,
9: CaptchaType.CAPY,
10: CaptchaType.TIKTOK,
11: CaptchaType.GEETESTV4
}
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC = {
CaptchaSolvingService.TWOCAPTCHA: {
1: get_http_resp_obj(dict(status=1, request='1234567890')),
2: get_http_resp_obj(dict(status=1, request='1234567890')),
3: get_http_resp_obj(dict(status=1, request='1234567890')),
4: get_http_resp_obj(dict(status=1, request='1234567890')),
5: get_http_resp_obj(dict(status=1, request='1234567890')),
6: get_http_resp_obj(dict(status=1, request='1234567890')),
7: get_http_resp_obj(dict(status=1, request='1234567890')),
8: get_http_resp_obj(dict(status=1, request='1234567890')),
9: get_http_resp_obj(dict(status=1, request='1234567890')),
10: get_http_resp_obj(dict(status=1, request='1234567890')),
11: get_http_resp_obj(dict(status=1, request='1234567890')),
},
CaptchaSolvingService.ANTI_CAPTCHA: {
1: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),
2: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),
3: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),
4: None,
5: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),
6: None,
7: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),
8: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),
9: None,
10: None,
11: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),
},
CaptchaSolvingService.AZCAPTCHA: {
1: get_http_resp_obj(dict(status=1, request='1234567890')),
2: get_http_resp_obj(dict(status=1, request='1234567890')),
3: get_http_resp_obj(dict(status=1, request='1234567890')),
4: None,
5: get_http_resp_obj(dict(status=1, request='1234567890')),
6: None,
7: None,
8: get_http_resp_obj(dict(status=1, request='1234567890')),
9: None,
10: None,
11: None,
},
CaptchaSolvingService.CPTCH_NET: {
1: get_http_resp_obj(dict(status=1, request='1234567890')),
2: get_http_resp_obj(dict(status=1, request='1234567890')),
3: get_http_resp_obj(dict(status=1, request='1234567890')),
4: None,
5: None,
6: None,
7: None,
8: None,
9: None,
10: None,
11: None,
},
CaptchaSolvingService.DEATHBYCAPTCHA: {
1: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),
2: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),
3: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),
4: None,
5: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),
6: None,
7: None,
8: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),
9: None,
10: None,
11: None,
}
}
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.RUCAPTCHA] = (
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.TWOCAPTCHA]
)
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC = {
CaptchaSolvingService.TWOCAPTCHA: {
1: dict(task_id='1234567890', extra={}),
2: dict(task_id='1234567890', extra={}),
3: dict(task_id='1234567890', extra={}),
4: dict(task_id='1234567890', extra={}),
5: dict(task_id='1234567890', extra={}),
6: dict(task_id='1234567890', extra={}),
7: dict(task_id='1234567890', extra={}),
8: dict(task_id='1234567890', extra={}),
9: dict(task_id='1234567890', extra={}),
10: dict(task_id='1234567890', extra={}),
11: dict(task_id='1234567890', extra={}),
},
CaptchaSolvingService.ANTI_CAPTCHA: {
1: dict(task_id='1234567890', extra={}),
2: dict(task_id='1234567890', extra={}),
3: dict(task_id='1234567890', extra={}),
4: None,
5: dict(task_id='1234567890', extra={}),
6: None,
7: dict(task_id='1234567890', extra={}),
8: dict(task_id='1234567890', extra={}),
9: None,
10: None,
11: dict(task_id='1234567890', extra={}),
},
CaptchaSolvingService.AZCAPTCHA: {
1: dict(task_id='1234567890', extra={}),
2: dict(task_id='1234567890', extra={}),
3: dict(task_id='1234567890', extra={}),
4: None,
5: dict(task_id='1234567890', extra={}),
6: None,
7: None,
8: dict(task_id='1234567890', extra={}),
9: None,
10: None,
11: None,
},
CaptchaSolvingService.CPTCH_NET: {
1: dict(task_id='1234567890', extra={}),
2: dict(task_id='1234567890', extra={}),
3: dict(task_id='1234567890', extra={}),
4: None,
5: None,
6: None,
7: None,
8: None,
9: None,
10: None,
11: None,
},
CaptchaSolvingService.DEATHBYCAPTCHA: {
1: dict(task_id='1234567890', extra={}),
2: dict(task_id='1234567890', extra={}),
3: dict(task_id='1234567890', extra={}),
4: None,
5: dict(task_id='1234567890', extra={}),
6: None,
7: None,
8: dict(task_id='1234567890', extra={}),
9: None,
10: None,
11: None,
}
}
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.RUCAPTCHA] = (
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.TWOCAPTCHA]
)
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC = {
1: exc.ServiceError,
2: exc.AccessDeniedError,
3: exc.LowBalanceError,
4: exc.ServiceTooBusy,
5: exc.TooManyRequestsError,
6: exc.MalformedRequestError,
7: exc.BadInputDataError,
8: exc.UnableToSolveError,
9: exc.ProxyError
}
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC = {
CaptchaSolvingService.TWOCAPTCHA: {
1: get_http_resp_obj(dict(status=0, request=choice(
['REPORT_NOT_RECORDED', 'ERROR_IP_ADDRES']))),
2: get_http_resp_obj(dict(status=0, request=choice(
['ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST', 'ERROR_IP_NOT_ALLOWED',
'IP_BANNED']))),
3: get_http_resp_obj(dict(status=0, request='ERROR_ZERO_BALANCE')),
4: get_http_resp_obj(dict(status=0, request='ERROR_NO_SLOT_AVAILABLE')),
5: get_http_resp_obj(dict(status=0, request=choice(
['MAX_USER_TURN', 'ERROR: 1001', 'ERROR: 1005']))),
6: get_http_resp_obj(dict(status=0, request=choice(
['ERROR_WRONG_ID_FORMAT', 'ERROR_WRONG_CAPTCHA_ID']))),
7: get_http_resp_obj(dict(status=0, request=choice(
'ERROR_UPLOAD ERROR_ZERO_CAPTCHA_FILESIZE ERROR_TOO_BIG_CAPTCHA_FILESIZE '
'ERROR_WRONG_FILE_EXTENSION ERROR_IMAGE_TYPE_NOT_SUPPORTED ERROR_PAGEURL '
'ERROR_BAD_TOKEN_OR_PAGEURL ERROR_GOOGLEKEY ERROR_BAD_PARAMETERS '
'ERROR_TOKEN_EXPIRED ERROR_EMPTY_ACTION'.split()))),
8: get_http_resp_obj(dict(status=0, request=choice(
'ERROR_CAPTCHAIMAGE_BLOCKED ERROR_CAPTCHA_UNSOLVABLE ERROR_BAD_DUPLICATES'.split()))),
9: get_http_resp_obj(dict(status=0, request=choice(
['ERROR_BAD_PROXY', 'ERROR_PROXY_CONNECTION_FAILED']))),
},
CaptchaSolvingService.ANTI_CAPTCHA: {
1: get_http_resp_obj(dict(errorId=2, errorCode='UNKNOWN_ERROR')),
2: get_http_resp_obj(dict(errorId=3, errorCode=choice(
['ERROR_KEY_DOES_NOT_EXIST', 'ERROR_IP_NOT_ALLOWED', 'ERROR_IP_BLOCKED']))),
3: get_http_resp_obj(dict(errorId=4, errorCode='ERROR_ZERO_BALANCE')),
4: get_http_resp_obj(dict(errorId=4, errorCode='ERROR_NO_SLOT_AVAILABLE')),
5: None,
6: get_http_resp_obj(dict(errorId=4, errorCode=choice(
'ERROR_NO_SUCH_METHOD ERROR_NO_SUCH_CAPCHA_ID ERROR_TASK_ABSENT '
'ERROR_TASK_NOT_SUPPORTED ERROR_FUNCAPTCHA_NOT_ALLOWED'.split()))),
7: get_http_resp_obj(dict(errorId=4, errorCode=choice(
'ERROR_ZERO_CAPTCHA_FILESIZE ERROR_TOO_BIG_CAPTCHA_FILESIZE '
'ERROR_IMAGE_TYPE_NOT_SUPPORTED ERROR_EMPTY_COMMENT ERROR_INCORRECT_SESSION_DATA '
'ERROR_RECAPTCHA_INVALID_SITEKEY ERROR_RECAPTCHA_INVALID_DOMAIN '
'ERROR_RECAPTCHA_OLD_BROWSER ERROR_TOKEN_EXPIRED ERROR_INVISIBLE_RECAPTCHA'.split()))),
8: get_http_resp_obj(dict(errorId=4, errorCode=choice(
'ERROR_CAPTCHA_UNSOLVABLE ERROR_BAD_DUPLICATES ERROR_RECAPTCHA_TIMEOUT '
'ERROR_FAILED_LOADING_WIDGET'.split()))),
9: get_http_resp_obj(dict(errorId=4, errorCode=choice(
'ERROR_PROXY_CONNECT_REFUSED ERROR_PROXY_CONNECT_TIMEOUT ERROR_PROXY_READ_TIMEOUT '
'ERROR_PROXY_BANNED ERROR_PROXY_TRANSPARENT ERROR_PROXY_HAS_NO_IMAGE_SUPPORT '
'ERROR_PROXY_INCOMPATIBLE_HTTP_VERSION ERROR_PROXY_NOT_AUTHORISED'.split()))),
},
CaptchaSolvingService.CPTCH_NET: {
1: get_http_resp_obj(dict(status=0, request='UNKNOWN_ERROR')),
2: get_http_resp_obj(dict(status=0, request=choice(
['ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST']))),
3: get_http_resp_obj(dict(status=0, request='ERROR_ZERO_BALANCE')),
4: None,
5: None,
6: get_http_resp_obj(dict(status=0, request='ERROR_WRONG_CAPTCHA_ID')),
7: get_http_resp_obj(dict(status=0, request=choice(
'ERROR_UPLOAD ERROR_ZERO_CAPTCHA_FILESIZE ERROR_TOO_BIG_CAPTCHA_FILESIZE '
'ERROR_PAGEURL ERROR_GOOGLEKEY ERROR'.split()))),
8: get_http_resp_obj(dict(status=0, request='ERROR_CAPTCHA_UNSOLVABLE')),
9: None,
},
CaptchaSolvingService.DEATHBYCAPTCHA: {
1: get_http_resp_obj(dict(status=255)),
2: get_http_resp_obj(dict(status=255, error=choice(
['token authentication disabled', 'not-logged-in', 'banned']))),
3: get_http_resp_obj(dict(status=255, error='insufficient-funds')),
4: get_http_resp_obj(dict(status=255, error='service-overload')),
5: None,
6: get_http_resp_obj(dict(status=255, error=choice(
['upload-failed', 'invalid-captcha']))),
7: get_http_resp_obj(dict(status=255, error=choice(
['ERROR_PAGEURL', 'Invalid base64-encoded CAPTCHA', 'Not a (CAPTCHA) image',
'Empty CAPTCHA image', 'ERROR_GOOGLEKEY', 'ERROR_PAGEURL',
'ERROR_PUBLICKEY', 'ERROR_SITEKEY', 'ERROR_ACTION', 'ERROR_MIN_SCORE',
'ERROR_MIN_SCORE_NOT_FLOAT']))),
8: None,
9: get_http_resp_obj(dict(status=255, error=choice(
['ERROR_PROXYTYPE', 'ERROR_PROXY']))),
}
}
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.RUCAPTCHA] = (
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.TWOCAPTCHA]
)
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.AZCAPTCHA] = (
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.TWOCAPTCHA]
)
================================================
FILE: tests/test_captcha_base.py
================================================
# -*- coding: UTF-8 -*-
from unicaps._captcha import CaptchaType
from unicaps._captcha.base import BaseCaptcha, BaseCaptchaSolution
def test_captcha_class_base(captcha_class):
"""
Checks base class of Captcha class.
"""
assert issubclass(captcha_class, BaseCaptcha)
def test_captcha_class_get_type(captcha_class):
"""
Checks get_type() function of Captcha class.
"""
assert isinstance(captcha_class.get_type(), CaptchaType)
def test_captcha_class_get_solution_class(captcha_class):
"""
Checks get_solution_class() function of Captcha class.
"""
assert issubclass(captcha_class.get_solution_class(), BaseCaptchaSolution)
def test_captcha_class_get_optional_data(captcha_instance):
"""
Checks get_optional_data() function of Captcha class.
"""
assert isinstance(captcha_instance.get_optional_data(), dict)
def test_captchasolution_class_get_type(captcha_class):
"""
Checks get_type() function of CaptchaSolution class.
"""
assert captcha_class.get_type() is captcha_class.get_solution_class().get_type()
def test_captchasolution_class_get_captcha_class(captcha_class):
"""
Checks get_type() function of CaptchaSolution class.
"""
assert captcha_class is captcha_class.get_solution_class().get_captcha_class()
def test_captchasolution_obj_get_string(captcha_class):
"""
Checks get_type() function of CaptchaSolution class.
"""
solution_class = captcha_class.get_solution_class()
dc_fields = solution_class.__dataclass_fields__
field_values = []
for i, field in enumerate(dc_fields.values(), start=1):
if field.type == str:
value = f'test{i}'
elif field.type == dict:
value = {f'test{i}': f'test{i}'}
else:
value = None
field_values.append(value)
solution_obj = solution_class(*field_values)
assert str(solution_obj) == '\n'.join(str(v) for v in field_values)
================================================
FILE: tests/test_image_captcha.py
================================================
# -*- coding: UTF-8 -*-
import base64
import os.path
import pathlib
import pytest
from unicaps.captcha import ImageCaptcha
from unicaps.exceptions import BadInputDataError
DATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')
IMAGE_FILE = os.path.join(DATA_DIR, 'image.jpg')
@pytest.fixture
def image_path():
return pathlib.Path(IMAGE_FILE)
@pytest.fixture
def image_bytes():
return open(IMAGE_FILE, 'rb').read()
@pytest.fixture
def image_base64(image_bytes):
return base64.b64encode(image_bytes)
def test_image_from_path(image_path, image_bytes):
""" Pathlib input test"""
captcha = ImageCaptcha(image=image_path)
assert image_bytes == captcha.get_image_bytes()
def test_image_from_binary(image_bytes):
""" Binary input test"""
captcha = ImageCaptcha(image=image_bytes)
assert image_bytes == captcha.get_image_bytes()
def test_not_an_image():
""" Not an image input test """
with pytest.raises(TypeError):
ImageCaptcha(image='wrong_image')
def test_bad_image():
""" Bad image input test """
with pytest.raises(BadInputDataError):
ImageCaptcha(image=b'bad_image_data')
================================================
FILE: tests/test_proxy.py
================================================
# -*- coding: UTF-8 -*-
"""
Proxy tests
"""
from unicaps.proxy import ProxyServer, ProxyServerType
def test_proxy_from_string():
proxy = ProxyServer('http://login:password@address:8080')
assert proxy.address == 'address'
assert proxy.proxy_type == ProxyServerType.HTTP
assert proxy.port == 8080
assert proxy.login == 'login'
assert proxy.password == 'password'
def test_proxy_wo_auth_from_string():
proxy = ProxyServer('https://address:8080')
assert proxy.address == 'address'
assert proxy.proxy_type == ProxyServerType.HTTPS
assert proxy.port == 8080
assert proxy.login is None
assert proxy.password is None
def test_proxy_address_only():
proxy = ProxyServer('address')
assert proxy.address == 'address'
assert proxy.proxy_type == ProxyServerType.HTTP
assert proxy.port == 80
assert proxy.login is None
assert proxy.password is None
def test_proxy_all():
proxy = ProxyServer('address', ProxyServerType.SOCKS4, 80, 'login', 'password')
assert proxy.address == 'address'
assert proxy.proxy_type == ProxyServerType.SOCKS4
assert proxy.port == 80
assert proxy.login == 'login'
assert proxy.password == 'password'
def test_proxy_to_string():
proxy = ProxyServer('address', ProxyServerType.SOCKS5, 80, 'login', 'password')
assert str(proxy) == 'socks5://login:password@address:80'
================================================
FILE: tests/test_service_module.py
================================================
# -*- coding: UTF-8 -*-
"""
Service module tests
"""
import importlib
import inspect
from copy import deepcopy
from unittest import mock
import pytest
from unicaps._captcha import CaptchaType
from unicaps._service import SOLVING_SERVICE
from data.data import (BASE_TASK_REQUEST_DATA, INPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC,
OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC,
INPUT_TEST_LIST_FOR_TASK_PARSE_RESPONSE_FUNC,
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC,
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC,
INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC,
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC)
from _helpers import dict_update
# dict of captcha services with lists of supported captchas
SERVICE_MODULES_FOR_TEST = {
'anti_captcha': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,
CaptchaType.FUNCAPTCHA, CaptchaType.GEETEST, CaptchaType.GEETESTV4,
CaptchaType.HCAPTCHA),
'twocaptcha': (CaptchaType.IMAGE, CaptchaType.TEXT, CaptchaType.RECAPTCHAV2,
CaptchaType.RECAPTCHAV3, CaptchaType.FUNCAPTCHA, CaptchaType.KEYCAPTCHA,
CaptchaType.GEETEST, CaptchaType.GEETESTV4, CaptchaType.HCAPTCHA,
CaptchaType.CAPY, CaptchaType.TIKTOK),
'rucaptcha': (CaptchaType.IMAGE, CaptchaType.TEXT, CaptchaType.RECAPTCHAV2,
CaptchaType.RECAPTCHAV3, CaptchaType.FUNCAPTCHA, CaptchaType.KEYCAPTCHA,
CaptchaType.GEETEST, CaptchaType.GEETESTV4, CaptchaType.HCAPTCHA,
CaptchaType.CAPY, CaptchaType.TIKTOK),
'azcaptcha': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,
CaptchaType.HCAPTCHA, CaptchaType.FUNCAPTCHA),
'cptch_net': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3),
'deathbycaptcha': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,
CaptchaType.HCAPTCHA, CaptchaType.FUNCAPTCHA)
}
BASE_REQUESTS = ('GetBalance', 'GetStatus', 'ReportGood', 'ReportBad')
TASK_REQUEST_PREPARE_PARAMS = ('self', 'captcha', 'proxy', 'user_agent', 'cookies')
SOLUTION_REQUEST_PREPARE_PARAMS = ('self', 'task')
REQUEST_PARSE_RESPONSE_PARAMS = ('self', 'response')
@pytest.fixture(scope="module", params=SERVICE_MODULES_FOR_TEST)
def service_module(request):
return importlib.import_module('unicaps._service.' + request.param)
@pytest.fixture(scope="module")
def service_instance(service_module):
return getattr(service_module, "Service")("test")
@pytest.fixture(scope="module")
def json_response_obj():
def get_obj(ret_value):
obj = mock.Mock()
obj.json = lambda: ret_value
return obj
return get_obj
def check_if_class_is_present(module, class_name, is_not=False):
if is_not:
assert not hasattr(module, class_name), \
f'{class_name} is found in service module {module.__name__}'
else:
assert hasattr(module, class_name), \
f'{class_name} is not found in service module {module.__name__}'
def is_captcha_supported(service_module, captcha_type):
module_name = service_module.__name__.split('.')[-1]
if captcha_type in SERVICE_MODULES_FOR_TEST[module_name]:
return True
return False
def get_request(service_module, captcha_type, req_type='Request'):
request_name = captcha_type.value + req_type
return request_name, getattr(service_module, request_name)
def test_if_service_class_is_present(service_module):
""" Checks if Service() class is declared in the module """
check_if_class_is_present(service_module, 'Service')
@pytest.mark.parametrize("req", BASE_REQUESTS)
def test_if_base_request_is_present(service_module, req):
""" Checks if all of the base requests are present in the module file """
check_if_class_is_present(service_module, req + 'Request')
@pytest.mark.parametrize("captcha_type", CaptchaType)
def test_if_task_request_is_present(service_module, captcha_type):
""" Checks if all of the task requests are present in the module file """
check_if_class_is_present(
service_module,
captcha_type.value + 'TaskRequest',
not is_captcha_supported(service_module, captcha_type)
)
@pytest.mark.parametrize("captcha_type", CaptchaType)
def test_if_solution_request_is_present(service_module, captcha_type):
""" Checks if all of the solution requests are present in the module file """
check_if_class_is_present(
service_module,
captcha_type.value + 'SolutionRequest',
not is_captcha_supported(service_module, captcha_type)
)
@pytest.mark.parametrize("req", BASE_REQUESTS)
def test_base_request_signature_of_prepare_func(service_module, req):
""" Checks signature of the <captcha>TaskRequest.prepare() function """
request_name = req + 'Request'
request_class = getattr(service_module, request_name)
if req in ('ReportGood', 'ReportBad'):
standard = ('self', 'solved_captcha')
else:
standard = ('self',)
params = tuple(inspect.signature(request_class.prepare).parameters)
assert params == standard, \
f"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}"
@pytest.mark.parametrize("captcha_type", CaptchaType)
def test_task_request_signature_of_prepare_func(service_module, captcha_type):
""" Checks signature of the <captcha>TaskRequest.prepare() function """
if is_captcha_supported(service_module, captcha_type):
request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')
params = tuple(inspect.signature(request_class.prepare).parameters)
assert params == TASK_REQUEST_PREPARE_PARAMS, \
f"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}"
@pytest.mark.parametrize("captcha_type", CaptchaType)
def test_solution_request_signature_of_prepare_func(service_module, captcha_type):
""" Checks signature of the <captcha>TaskRequest.prepare() function """
if is_captcha_supported(service_module, captcha_type):
request_name, request_class = get_request(service_module, captcha_type, 'SolutionRequest')
params = tuple(inspect.signature(request_class.prepare).parameters)
assert params == SOLUTION_REQUEST_PREPARE_PARAMS, \
f"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}"
@pytest.mark.parametrize("req", BASE_REQUESTS)
def test_base_request_signature_of_parse_response_func(service_module, req):
""" Checks signature of the <captcha>TaskRequest.prepare() function """
request_name = req + 'Request'
request_class = getattr(service_module, request_name)
params = tuple(inspect.signature(request_class.parse_response).parameters)
assert params == REQUEST_PARSE_RESPONSE_PARAMS, \
f"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}"
@pytest.mark.parametrize("captcha_type, req_type",
[(t, r) for t in CaptchaType for r in ('TaskRequest', 'SolutionRequest')])
def test_captcha_request_signature_of_parse_response_func(service_module, captcha_type, req_type):
"""
Checks signature of the <captcha>TaskRequest.parse_response() and
<captcha>SolutionRequest.parse_response() functions.
"""
if not is_captcha_supported(service_module, captcha_type):
pytest.skip("CAPTCHA is not supported!")
request_name, request_class = get_request(service_module, captcha_type, req_type)
params = tuple(inspect.signature(request_class.parse_response).parameters)
assert params == REQUEST_PARSE_RESPONSE_PARAMS, \
f"Incorrect signature of {request_name}.parse_response() func: {', '.join(params)}"
@pytest.mark.parametrize("test_id,input_data", INPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC.items())
def test_task_request_return_value_of_prepare_func(service_module, test_id, input_data):
service_type = {v: k for k, v in SOLVING_SERVICE.items()}[service_module]
captcha_type = input_data[0].get_type()
if not is_captcha_supported(service_module, captcha_type):
pytest.skip("CAPTCHA is not supported!")
request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')
# service instance
service = service_module.Service('test')
# request instance
request_instance = request_class(service)
result_dict = request_instance.prepare(*input_data)
standard_result = deepcopy(BASE_TASK_REQUEST_DATA[service_type])
dict_update(standard_result, OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC[service_type][test_id])
assert result_dict == standard_result
@pytest.mark.parametrize("test_id,captcha_type",
INPUT_TEST_LIST_FOR_TASK_PARSE_RESPONSE_FUNC.items())
def test_task_request_return_value_of_parse_response_func(service_module, test_id, captcha_type):
service_type = {v: k for k, v in SOLVING_SERVICE.items()}[service_module]
if not is_captcha_supported(service_module, captcha_type):
pytest.skip("CAPTCHA is not supported!")
request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')
# service instance
service = service_module.Service('test')
input_data = INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[service_type][test_id]
# request instance
request_instance = request_class(service)
result_dict = request_instance.parse_response(input_data)
standard_result = deepcopy(OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[service_type][test_id])
assert result_dict == standard_result
@pytest.mark.parametrize("test_id,captcha_type,exc_type",
[(i, c, e) for i, e in
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC.items()
for c in CaptchaType])
def test_task_request_exception_of_parse_response_func(service_module, test_id, captcha_type,
exc_type):
service_type = {v: k for k, v in SOLVING_SERVICE.items()}[service_module]
if not is_captcha_supported(service_module, captcha_type):
pytest.skip("CAPTCHA is not supported!")
request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')
# service instance
service = service_module.Service('test')
# get input data, skip test
input_data = INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[service_type][test_id]
if not input_data:
pytest.skip("The service doesn't support the testing exception!")
# request instance
request_instance = request_class(service)
expected_exception = (
OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[test_id]
)
with pytest.raises(expected_exception):
request_instance.parse_response(input_data)
================================================
FILE: tests/test_solver.py
================================================
# -*- coding: UTF-8 -*-
"""
CaptchaSolver tests
"""
from unittest.mock import Mock
import pytest
from unicaps import CaptchaSolver, CaptchaSolvingService
from unicaps.captcha import CaptchaType
API_KEY = 'TEST_API_KEY'
@pytest.fixture(scope="module")
def captcha_solver():
return CaptchaSolver('2captcha.com', API_KEY)
@pytest.fixture()
def mocked_captcha_solver(captcha_solver, monkeypatch):
monkeypatch.setattr(captcha_solver, '_service', Mock())
return captcha_solver
def test_solver_init():
service = CaptchaSolvingService.ANTI_CAPTCHA
solver = CaptchaSolver(service, API_KEY)
assert solver.service_name == service
assert solver.api_key == API_KEY
def test_solver_init_from_string():
solver = CaptchaSolver('2captcha.com', API_KEY)
assert solver.service_name == CaptchaSolvingService.TWOCAPTCHA
assert solver.api_key == API_KEY
def test_solver_bad_init():
with pytest.raises(ValueError):
CaptchaSolver('2captcha', API_KEY)
def test_solver_bad_init2():
with pytest.raises(ValueError):
CaptchaSolver(b'2captcha.com', API_KEY)
# @pytest.mark.parametrize("captcha_type", CaptchaType)
def test_call_solve_func(mocked_captcha_solver, captcha_instance):
mapping = {
CaptchaType.IMAGE: 'image_captcha',
CaptchaType.TEXT: 'text_captcha',
CaptchaType.RECAPTCHAV2: 'recaptcha_v2',
CaptchaType.RECAPTCHAV3: 'recaptcha_v3',
CaptchaType.HCAPTCHA: 'hcaptcha',
CaptchaType.FUNCAPTCHA: 'funcaptcha',
CaptchaType.KEYCAPTCHA: 'keycaptcha',
CaptchaType.GEETEST: 'geetest',
CaptchaType.CAPY: 'capy_puzzle',
CaptchaType.TIKTOK: 'tiktok',
CaptchaType.GEETESTV4: 'geetest_v4'
}
func = getattr(mocked_captcha_solver, f'solve_{mapping[captcha_instance.get_type()]}')
func(
**{k: v for k, v in captcha_instance.__dict__.items() if not k.startswith('_')}
)
================================================
FILE: unicaps/__init__.py
================================================
# -*- coding: UTF-8 -*-
"""
Unicaps package
~~~~~~~~~~~~~~~
"""
# pylint: disable=unused-import,import-error
from ._solver import CaptchaSolver
from ._solver_async import AsyncCaptchaSolver
from ._service import CaptchaSolvingService
__all__ = ('CaptchaSolver', 'AsyncCaptchaSolver', 'CaptchaSolvingService')
================================================
FILE: unicaps/__version__.py
================================================
""" Version info """
__title__ = 'unicaps'
__description__ = 'Python CAPTCHA solving for Humans.'
__url__ = 'https://github.com/sergey-scat/unicaps'
__version__ = '1.3.0'
__author__ = 'Sergey Scat'
__license__ = 'Apache 2.0'
__copyright__ = 'Copyright 2020-2024 Sergey Scat'
================================================
FILE: unicaps/_captcha/__init__.py
================================================
# -*- coding: UTF-8 -*-
""" CAPTCHAs """
# pylint: disable=unused-import,import-error
from .image import ImageCaptcha
from .text import TextCaptcha
from .recaptcha_v2 import RecaptchaV2
from .recaptcha_v3 import RecaptchaV3
from .hcaptcha import HCaptcha
from .funcaptcha import FunCaptcha
from .keycaptcha import KeyCaptcha
from .geetest import GeeTest
from .geetest_v4 import GeeTestV4
from .capy import CapyPuzzle
from .tiktok import TikTokCaptcha
from .base import CaptchaType
__all__ = (
'ImageCaptcha',
'TextCaptcha',
'RecaptchaV2',
'RecaptchaV3',
'HCaptcha',
'FunCaptcha',
'KeyCaptcha',
'GeeTest',
'GeeTestV4',
'CapyPuzzle',
'TikTokCaptcha',
'CaptchaType'
)
================================================
FILE: unicaps/_captcha/base.py
================================================
# -*- coding: UTF-8 -*-
"""
Base CAPTCHA stuff
"""
import enum
import importlib
from abc import ABC
from dataclasses import asdict, dataclass, fields, MISSING
from typing import Dict
class CaptchaType(enum.Enum):
""" Captcha type enumeration """
IMAGE = "ImageCaptcha"
RECAPTCHAV2 = "RecaptchaV2"
RECAPTCHAV3 = "RecaptchaV3"
TEXT = "TextCaptcha"
FUNCAPTCHA = "FunCaptcha"
GEETEST = "GeeTest"
GEETESTV4 = "GeeTestV4"
HCAPTCHA = "HCaptcha"
KEYCAPTCHA = "KeyCaptcha"
CAPY = "CapyPuzzle"
TIKTOK = "TikTokCaptcha"
@dataclass
class BaseCaptcha(ABC):
""" Base class for any CAPTCHA """
@classmethod
def get_type(cls) -> CaptchaType:
""" Return CaptchaType """
return CaptchaType(cls.__name__)
@classmethod
def get_solution_class(cls) -> 'BaseCaptchaSolution':
""" Return appropriate solution class """
return getattr(importlib.import_module(cls.__module__), cls.__name__ + "Solution")
def get_optional_data(self, **kwargs) -> Dict:
"""
Return a dict with all optional fields requested (that are not None)
as a dict with given names.
:return: :dict:Dictionary of optional not None fields with given names and those values
:rtype: dict
"""
result = {}
if not kwargs:
# get all optional params
kwargs = {
field.name: (field.name, None) for field in fields(self)
if field.default is not MISSING
}
for opt_field in kwargs:
opt_field_value = getattr(self, opt_field)
field_name, converter = kwargs[opt_field]
if opt_field_value is not None:
if callable(converter):
opt_field_value = converter(opt_field_value)
result[field_name] = opt_field_value
return result
@dataclass
class BaseCaptchaSolution(ABC):
""" Base class for any CAPTCHA solution """
@classmethod
def get_type(cls) -> CaptchaType:
""" Returns CaptchaType """
return CaptchaType(cls.__name__.split("Solution", maxsplit=1)[0])
@classmethod
def get_captcha_class(cls) -> BaseCaptcha:
""" Returns appropriate solution class """
return getattr(
importlib.import_module(cls.__module__),
cls.__name__.split("Solution", maxsplit=1)[0]
)
def __str__(self):
return '\n'.join(str(getattr(self, field.name)) for field in fields(self))
def as_dict(self):
""" Get solution data as Python dictionary """
return asdict(self)
================================================
FILE: unicaps/_captcha/capy.py
================================================
# -*- coding: UTF-8 -*-
"""
Capy Puzzle
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class CapyPuzzle(BaseCaptcha):
""" Capy Puzzle CAPTCHA """
site_key: str
page_url: str
api_server: Optional[str] = None
@enforce_types
@dataclass
class CapyPuzzleSolution(BaseCaptchaSolution):
""" Capy Puzzle CAPTCHA solution """
captchakey: str
challengekey: str
answer: str
================================================
FILE: unicaps/_captcha/funcaptcha.py
================================================
# -*- coding: UTF-8 -*-
"""
FunCaptcha
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class FunCaptcha(BaseCaptcha):
""" FunCaptcha """
public_key: str
page_url: str
service_url: Optional[str] = None
no_js: Optional[bool] = None
blob: Optional[str] = None
@enforce_types
@dataclass
class FunCaptchaSolution(BaseCaptchaSolution):
""" FunCaptcha solution """
token: str
================================================
FILE: unicaps/_captcha/geetest.py
================================================
# -*- coding: UTF-8 -*-
"""
GeeTest CAPTCHA
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class GeeTest(BaseCaptcha):
""" GeeTest """
page_url: str
gt_key: str
challenge: str
api_server: Optional[str] = None
@enforce_types
@dataclass
class GeeTestSolution(BaseCaptchaSolution):
""" GeeTest solution """
challenge: str
validate: str
seccode: str
================================================
FILE: unicaps/_captcha/geetest_v4.py
================================================
# -*- coding: UTF-8 -*-
"""
GeeTest v4 CAPTCHA
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class GeeTestV4(BaseCaptcha):
""" GeeTest v4 """
page_url: str
captcha_id: str
@enforce_types
@dataclass
class GeeTestV4Solution(BaseCaptchaSolution):
""" GeeTest v4 solution """
captcha_id: str
lot_number: str
pass_token: str
gen_time: str
captcha_output: str
================================================
FILE: unicaps/_captcha/hcaptcha.py
================================================
# -*- coding: UTF-8 -*-
"""
hCaptcha
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class HCaptcha(BaseCaptcha):
""" hCaptcha """
site_key: str
page_url: str
is_invisible: bool = False
api_domain: Optional[str] = None
@enforce_types
@dataclass
class HCaptchaSolution(BaseCaptchaSolution):
""" hCaptcha solution """
token: str
================================================
FILE: unicaps/_captcha/image.py
================================================
# -*- coding: UTF-8 -*-
"""
Image CAPTCHA
"""
import base64
import imghdr
import io
import pathlib
from dataclasses import dataclass
from typing import Union, Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
from ..common import CaptchaAlphabet, CaptchaCharType, WorkerLanguage
from ..exceptions import BadInputDataError
@enforce_types
@dataclass
class ImageCaptcha(BaseCaptcha):
""" Image CAPTCHA """
image: Union[bytes, io.RawIOBase, io.BufferedIOBase, pathlib.Path]
char_type: Optional[CaptchaCharType] = None
is_phrase: Optional[bool] = None
is_case_sensitive: Optional[bool] = None
is_math: Optional[bool] = None
min_len: Optional[int] = None
max_len: Optional[int] = None
alphabet: Optional[CaptchaAlphabet] = None
language: Optional[WorkerLanguage] = None
comment: Optional[str] = None
def __post_init__(self):
self._image_bytes = None
self.get_image_bytes()
def get_image_bytes(self) -> bytes:
""" Bytes image """
if self._image_bytes is None:
if isinstance(self.image, bytes):
self._image_bytes = self.image # type: ignore
elif isinstance(self.image, (io.RawIOBase, io.BufferedIOBase)):
self._image_bytes = self.image.read()
elif isinstance(self.image, pathlib.Path):
self._image_bytes = self.image.read_bytes()
# check image type
self.get_image_type()
return self._image_bytes
def get_image_base64(self) -> bytes:
""" BASE64 image """
return base64.b64encode(self.get_image_bytes())
def get_image_type(self) -> str:
""" Get type of image file/data """
image_type = imghdr.what(None, h=self._image_bytes)
if not image_type:
raise BadInputDataError("Unable to recognize image type!")
return image_type
@enforce_types
@dataclass
class ImageCaptchaSolution(BaseCaptchaSolution):
""" Image CAPTCHA solution """
text: str
================================================
FILE: unicaps/_captcha/keycaptcha.py
================================================
# -*- coding: UTF-8 -*-
"""
KeyCaptcha
"""
from dataclasses import dataclass
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class KeyCaptcha(BaseCaptcha):
""" KeyCaptcha """
page_url: str
user_id: str
session_id: str
ws_sign: str
ws_sign2: str
@enforce_types
@dataclass
class KeyCaptchaSolution(BaseCaptchaSolution):
""" KeyCaptcha solution """
token: str
================================================
FILE: unicaps/_captcha/recaptcha_v2.py
================================================
# -*- coding: UTF-8 -*-
"""
Google reCAPTCHA v2
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class RecaptchaV2(BaseCaptcha):
""" Google reCAPTCHA v2 """
site_key: str
page_url: str
is_invisible: bool = False
is_enterprise: bool = False
data_s: Optional[str] = None
api_domain: Optional[str] = None
@enforce_types
@dataclass
class RecaptchaV2Solution(BaseCaptchaSolution):
""" Google reCAPTCHA v2 solution """
token: str
================================================
FILE: unicaps/_captcha/recaptcha_v3.py
================================================
# -*- coding: UTF-8 -*-
"""
Google reCAPTCHA v3
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class RecaptchaV3(BaseCaptcha):
""" Google reCAPTCHA v3 """
site_key: str
page_url: str
is_enterprise: bool = False
action: Optional[str] = None
min_score: Optional[float] = None
api_domain: Optional[str] = None
@enforce_types
@dataclass
class RecaptchaV3Solution(BaseCaptchaSolution):
""" Google reCAPTCHA v3 solution """
token: str
================================================
FILE: unicaps/_captcha/text.py
================================================
# -*- coding: UTF-8 -*-
"""
Text CAPTCHA
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
from ..common import CaptchaAlphabet, WorkerLanguage
@enforce_types
@dataclass
class TextCaptcha(BaseCaptcha):
""" Text CAPTCHA """
text: str
alphabet: Optional[CaptchaAlphabet] = None
language: Optional[WorkerLanguage] = None
@enforce_types
@dataclass
class TextCaptchaSolution(BaseCaptchaSolution):
""" Text CAPTCHA solution """
text: str
================================================
FILE: unicaps/_captcha/tiktok.py
================================================
# -*- coding: UTF-8 -*-
"""
TikTokCaptcha
"""
from dataclasses import dataclass
from typing import Optional
from enforce_typing import enforce_types # type: ignore
from .base import BaseCaptcha, BaseCaptchaSolution
@enforce_types
@dataclass
class TikTokCaptcha(BaseCaptcha):
""" TikTokCaptcha """
page_url: str
aid: Optional[int] = None
host: Optional[str] = None
@enforce_types
@dataclass
class TikTokCaptchaSolution(BaseCaptchaSolution):
""" TikTokCaptcha solution """
cookies: dict
================================================
FILE: unicaps/_misc/__init__.py
================================================
# -*- coding: UTF-8 -*-
"""
Miscellaneous stuff
"""
================================================
FILE: unicaps/_misc/proxy.py
================================================
# -*- coding: UTF-8 -*-
"""
Proxy Server representation
"""
import socket
from dataclasses import dataclass
from enum import Enum
from typing import Optional
def _is_ip_address(value):
try:
socket.inet_aton(value)
return True
except socket.error:
return False
class ProxyServerType(Enum):
""" Type of proxy server """
HTTP = 'http'
HTTPS = 'https'
SOCKS4 = 'socks4'
SOCKS5 = 'socks5'
@dataclass
class ProxyServer:
""" Represents Proxy server """
address: str
proxy_type: ProxyServerType = ProxyServerType.HTTP
port: int = 80
login: Optional[str] = None
password: Optional[str] = None
def __post_init__(self):
proxy_string = self.address
if '://' in proxy_string:
proxy_type, proxy_string = proxy_string.split('://')
self.proxy_type = ProxyServerType(proxy_type.lower())
if '@' in proxy_string:
credentials, proxy_string = proxy_string.split('@')
self.login, self.password = credentials.split(':', maxsplit=1)
if ':' in proxy_string:
self.address, port = proxy_string.split(':', maxsplit=1)
self.port = int(port)
else:
self.address = proxy_string
def __str__(self):
return self.get_string(including_type=True)
def get_string(self, including_type=False):
""" Get proxy as string like [<type>://][<login>:<password>@]<addr>:<port> """
proxy_string = ''
if including_type:
proxy_string += self.proxy_type.value + '://'
if self.login:
proxy_string += self.login + ':' + self.password + '@'
return proxy_string + self.address + ':' + str(self.port)
def get_ip_address(self):
""" Get IP address by hostname """
if not _is_ip_address(self.address):
return socket.gethostbyname(self.address)
return self.address
================================================
FILE: unicaps/_service/__init__.py
================================================
# -*- coding: UTF-8 -*-
"""
Certain services related stuff
"""
import enum
# pylint: disable=import-self
from . import (
anti_captcha, azcaptcha, captcha_guru, cptch_net, deathbycaptcha, rucaptcha, twocaptcha
)
class CaptchaSolvingService(enum.Enum):
""" CAPTCHA solving service enumeration """
ANTI_CAPTCHA = "anti-captcha.com"
AZCAPTCHA = "azcaptcha.com"
CAPTCHA_GURU = "cap.guru"
CPTCH_NET = "cptch.net"
DEATHBYCAPTCHA = "deathbycaptcha.com"
RUCAPTCHA = "rucaptcha.com"
TWOCAPTCHA = "2captcha.com"
# supported CAPTCHA solving services
SOLVING_SERVICE = {
CaptchaSolvingService.ANTI_CAPTCHA: anti_captcha,
CaptchaSolvingService.AZCAPTCHA: azcaptcha,
CaptchaSolvingService.CAPTCHA_GURU: captcha_guru,
CaptchaSolvingService.CPTCH_NET: cptch_net,
CaptchaSolvingService.DEATHBYCAPTCHA: deathbycaptcha,
CaptchaSolvingService.RUCAPTCHA: rucaptcha,
CaptchaSolvingService.TWOCAPTCHA: twocaptcha
}
================================================
FILE: unicaps/_service/anti_captcha.py
================================================
# -*- coding: UTF-8 -*-
"""
anti-captcha.com service
"""
import json
from .base import HTTPService
from .._transport.http_transport import HTTPRequestJSON # type: ignore
from .. import exceptions
from .._captcha import CaptchaType
from ..common import WorkerLanguage
__all__ = [
'Service', 'GetBalanceRequest', 'GetStatusRequest',
'ReportGoodRequest', 'ReportBadRequest',
'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',
'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',
'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest',
'FunCaptchaTaskRequest', 'FunCaptchaSolutionRequest',
'GeeTestTaskRequest', 'GeeTestSolutionRequest',
'GeeTestV4TaskRequest', 'GeeTestV4SolutionRequest',
'HCaptchaTaskRequest', 'HCaptchaSolutionRequest',
]
class Service(HTTPService):
""" Main service class for anti-captcha """
BASE_URL = 'https://api.anti-captcha.com'
def _post_init(self):
""" Init settings """
for captcha_type in self.settings:
self.settings[captcha_type].polling_interval = 2
if captcha_type in (CaptchaType.IMAGE,):
self.settings[captcha_type].polling_delay = 5
self.settings[captcha_type].solution_timeout = 90
else:
self.settings[captcha_type].polling_delay = 10
self.settings[captcha_type].solution_timeout = 300
class Request(HTTPRequestJSON):
""" Common Request class for anti-captcha """
def prepare(self, **kwargs) -> dict:
""" Prepares request """
request = super().prepare(**kwargs)
request.update(
dict(
method="POST",
json=dict(clientKey=self._service.api_key)
)
)
return request
def parse_response(self, response) -> dict:
""" Parses response and checks for errors """
response_data = super().parse_response(response)
error_id = response_data.pop("errorId")
if error_id == 0:
return response_data
# ############# #
# handle errors #
# ############# #
error_code = response_data.get("errorCode", f'ERROR {error_id}')
error_text = response_data.get("errorDescription", "")
error_msg = f"{error_code}: {error_text}"
# pylint: disable=no-else-raise
if error_code in ('ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST',
'ERROR_IP_NOT_ALLOWED', 'ERROR_IP_BLOCKED'):
raise exceptions.AccessDeniedError(error_msg)
elif error_code in ('ERROR_ZERO_BALANCE',):
raise exceptions.LowBalanceError(error_msg)
elif error_code in ('ERROR_NO_SLOT_AVAILABLE',):
raise exceptions.ServiceTooBusy(error_msg)
elif error_code in ('ERROR_NO_SUCH_METHOD', 'ERROR_NO_SUCH_CAPCHA_ID', 'ERROR_TASK_ABSENT',
'ERROR_TASK_NOT_SUPPORTED', 'ERROR_FUNCAPTCHA_NOT_ALLOWED'):
raise exceptions.MalformedRequestError(error_msg)
elif error_code in ('ERROR_ZERO_CAPTCHA_FILESIZE', 'ERROR_TOO_BIG_CAPTCHA_FILESIZE',
'ERROR_WRONG_FILE_EXTENSION', 'ERROR_IMAGE_TYPE_NOT_SUPPORTED',
'ERROR_UPLOAD', 'ERROR_PAGEURL', 'ERROR_BAD_TOKEN_OR_PAGEURL',
'ERROR_GOOGLEKEY', 'ERROR_EMPTY_COMMENT',
'ERROR_INCORRECT_SESSION_DATA', 'ERROR_RECAPTCHA_INVALID_SITEKEY',
'ERROR_RECAPTCHA_INVALID_DOMAIN', 'ERROR_RECAPTCHA_OLD_BROWSER',
'ERROR_TOKEN_EXPIRED', 'ERROR_INVISIBLE_RECAPTCHA'):
raise exceptions.BadInputDataError(error_msg)
elif error_code in ('ERROR_CAPTCHAIMAGE_BLOCKED', 'ERROR_CAPTCHA_UNSOLVABLE',
'ERROR_BAD_DUPLICATES', 'ERROR_RECAPTCHA_TIMEOUT',
'ERROR_FAILED_LOADING_WIDGET'):
raise exceptions.UnableToSolveError(error_msg)
elif error_code in ('ERROR_PROXY_CONNECT_REFUSED', 'ERROR_PROXY_CONNECT_TIMEOUT',
'ERROR_PROXY_READ_TIMEOUT', 'ERROR_PROXY_BANNED',
'ERROR_PROXY_TRANSPARENT', 'ERROR_PROXY_HAS_NO_IMAGE_SUPPORT',
'ERROR_PROXY_INCOMPATIBLE_HTTP_VERSION', 'ERROR_PROXY_NOT_AUTHORISED'):
raise exceptions.ProxyError(error_msg)
raise exceptions.ServiceError(error_msg)
class GetBalanceRequest(Request):
""" GetBalance Request class """
def prepare(self) -> dict: # type: ignore
""" Prepares request """
request = super().prepare()
request.update(dict(url=self._service.BASE_URL + "/getBalance"))
return request
def parse_response(self, response) -> dict:
""" Parses response and returns task_id """
return dict(balance=float(super().parse_response(response)['balance']))
class GetStatusRequest(GetBalanceRequest):
""" GetStatus Request class """
def parse_response(self, response) -> dict:
""" Parses response and returns task_id """
try:
return super().parse_response(response)
except exceptions.UnicapsException:
return {}
class ReportGoodRequest(Request):
""" ReportGood Request class """
# pylint: disable=arguments-differ
def prepare(self, solved_captcha) -> dict: # type: ignore
""" Prepares request """
raise exceptions.UnicapsException(
"Report for good CAPTCHA is not supported by the current service!"
)
class ReportBadRequest(Request):
""" ReportBad Request class """
# pylint: disable=arguments-differ
def prepare(self, solved_captcha) -> dict: # type: ignore
""" Prepares request """
request = super().prepare(solved_captcha=solved_captcha)
captcha_type = solved_captcha.task.captcha.get_type()
if captcha_type == CaptchaType.IMAGE:
uri = "/reportIncorrectImageCaptcha"
elif captcha_type in (CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3):
uri = "/reportIncorrectRecaptcha"
else:
raise exceptions.UnicapsException(
f"Report for bad {captcha_type.value} is not supported!"
)
request.update(dict(url=self._service.BASE_URL + uri))
request["json"].update(dict(taskId=int(solved_captcha.captcha_id)))
return request
class TaskRequest(Request):
""" Request class for requests to /createTask """
# pylint: disable=arguments-differ,unused-argument
def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # type: ignore
""" Prepare a request """
request = super().prepare(
captcha=captcha,
proxy=proxy,
user_agent=user_agent,
cookies=cookies
)
request.update(dict(url=self._service.BASE_URL + "/createTask"))
request["json"].update(
dict(task={},
softId=940)
)
# add proxy
if proxy:
request["json"]["task"].update(
dict(
proxyType=proxy.proxy_type.value,
# Anti-captcha supports IP addresses only
proxyAddress=proxy.get_ip_address(),
proxyPort=proxy.port
)
)
if proxy.login:
request["json"]["task"].update(
dict(
proxyLogin=proxy.login,
proxyPassword=proxy.password
)
)
if user_agent:
request["json"]["task"]["userAgent"] = user_agent
if cookies:
request["json"]["task"]["cookies"] = '; '.join(f'{k}={v}'for k, v in cookies.items())
return request
def parse_response(self, response) -> dict:
""" Parses response and returns task_id """
response_data = super().parse_response(response)
return {"task_id": response_data.pop("taskId"),
"extra": response_data}
class SolutionRequest(Request):
""" Request class for requests to /getTaskResult """
# pylint: disable=arguments-differ
def prepare(self, task) -> dict: # type: ignore
""" Prepare a request """
request = super().prepare(task=task)
request.update(dict(url=self._service.BASE_URL + "/getTaskResult"))
request["json"].update(dict(taskId=str(task.task_id)))
return request
def parse_response(self, response) -> dict:
""" Parses response and returns solution and cost """
response_data = super().parse_response(response)
if response_data["status"] != "ready":
raise exceptions.SolutionNotReadyYet()
solution_data = response_data["solution"]
solution_class = self.source_data['task'].captcha.get_solution_class()
captcha_type = self.source_data['task'].captcha.get_type()
args = []
kwargs = {}
if captcha_type in (CaptchaType.IMAGE,):
args.append(solution_data.pop('text'))
elif captcha_type in (CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,
CaptchaType.HCAPTCHA):
args.append(solution_data.pop('gRecaptchaResponse'))
elif captcha_type in (CaptchaType.FUNCAPTCHA,):
args.append(solution_data.pop('token'))
elif captcha_type in (CaptchaType.GEETEST, CaptchaType.GEETESTV4):
kwargs.update(solution_data)
else:
kwargs.update(solution_data)
solution = solution_class(*args, **kwargs)
if "cost" in response_data:
cost = response_data.pop("cost")
else:
cost = None
return dict(
solution=solution,
cost=cost,
extra=response_data
)
class ImageCaptchaTaskRequest(TaskRequest):
""" ImageCaptchaTask Request class """
# pylint: disable=arguments-differ,signature-differs
def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # type: ignore
""" Prepare a request """
request = super().prepare(
captcha=captcha,
proxy=None,
user_agent=None,
cookies=None
)
task_data = dict(
type="ImageToTextTask",
body=captcha.get_image_base64().decode('ascii')
)
task_data.update(
captcha.get_optional_data(
is_case_sensitive=('case', None),
is_phrase=('phrase', None),
is_math=('math', None),
char_type=('numeric', lambda v: v.value if v.value in (1, 2) else None),
min_len=('minLength', None),
max_len=('maxLength', None),
comment=('comment', None)
)
)
request['json']['task'].update(task_data)
# set workers pool language
if captcha.language:
request['json']['languagePool'] = (
'rn' if captcha.language == WorkerLanguage.RUSSIAN else 'en'
)
return request
class ImageCaptchaSolutionRequest(SolutionRequest):
""" Image CAPTCHA solution request """
class RecaptchaV2TaskRequest(TaskRequest):
""" reCAPTCHA v2 task Request class """
# pylint: disable=arguments-differ,signature-differs
def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # type: ignore
""" Prepares request """
if proxy:
kwargs = dict(captcha=captcha, proxy=proxy, user_agent=user_agent, cookies=cookies)
task_type = "RecaptchaV2EnterpriseTask" if captcha.is_enterprise else "NoCaptchaTask"
else:
kwargs = dict(captcha=captcha, proxy=None, user_agent=None, cookies=None)
task_type = ("RecaptchaV2EnterpriseTaskProxyless" if captcha.is_enterprise
else "NoCaptchaTaskProxyless")
request = super().prepare(**kwargs)
request['json']['task'].update(
dict(
type=task_type,
websiteURL=captcha.page_url,
websiteKey=captcha.site_key,
isInvisible=captcha.is_invisible
)
)
# if enterprise captcha
if captcha.is_enterprise:
request['json']['task']['enterprisePayload'] = dict(s=captcha.data_s)
else:
# set optional data if any
request['json']['task'].update(
captcha.get_optional_data(
data_s=('recaptchaDataSValue', None)
)
)
# set optional api_domain if any
request['json']['task'].update(
captcha.get_optional_data(
api_domain=('apiDomain', None)
)
)
return request
class RecaptchaV2SolutionRequest(SolutionRequest):
""" reCAPTCHA v2 solution request """
class RecaptchaV3TaskRequest(TaskRequest):
""" reCAPTCHA v3 task Request class """
# pylint: disable=arguments-differ,signature-differs
def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # type: ignore
""" Prepares request """
request = super().prepare(
captcha=captcha,
proxy=None,
user_agent=None,
cookies=None
)
request['json']['task'].update(
dict(
type="RecaptchaV3TaskProxyless",
websiteURL=captcha.page_url,
websiteKey=captcha.site_key
)
)
# set optional data if any
request['json']['task'].update(
captcha.get_optional_data(
min_score=('minScore', None),
action=('pageAction', None),
api_domain=('apiDomain', None)
)
)
# if enterprise captcha
if captcha.is_enterprise:
request['json']['task']['isEnterprise'] = True
return request
class RecaptchaV3SolutionRequest(SolutionRequest):
""" reCAPTCHA v3 solution request """
class FunCaptchaTaskRequest(TaskRequest):
gitextract_x1qvq890/
├── .github/
│ └── workflows/
│ ├── python-package.yml
│ └── python-publish.yml
├── .gitignore
├── LICENSE
├── README.md
├── acceptance_tests.py
├── examples/
│ ├── README.MD
│ ├── async_capy_puzzle.py
│ ├── async_funcaptcha.py
│ ├── async_geetest.py
│ ├── async_geetest_v4.py
│ ├── async_hcaptcha.py
│ ├── async_image.py
│ ├── async_keycaptcha.py
│ ├── async_recaptcha_v2.py
│ ├── async_recaptcha_v2_enterprise.py
│ ├── async_recaptcha_v2_invisible.py
│ ├── async_recaptcha_v3.py
│ ├── async_text.py
│ ├── capy_puzzle.py
│ ├── funcaptcha.py
│ ├── geetest.py
│ ├── geetest_v4.py
│ ├── hcaptcha.py
│ ├── image.py
│ ├── keycaptcha.py
│ ├── recaptcha_v2.py
│ ├── recaptcha_v2_enterprise.py
│ ├── recaptcha_v2_invisible.py
│ ├── recaptcha_v3.py
│ ├── requirements.txt
│ ├── run_all.py
│ └── text.py
├── requirements-dev.txt
├── requirements.txt
├── setup.cfg
├── setup.py
├── tests/
│ ├── _helpers.py
│ ├── conftest.py
│ ├── data/
│ │ ├── __init__.py
│ │ └── data.py
│ ├── test_captcha_base.py
│ ├── test_image_captcha.py
│ ├── test_proxy.py
│ ├── test_service_module.py
│ └── test_solver.py
└── unicaps/
├── __init__.py
├── __version__.py
├── _captcha/
│ ├── __init__.py
│ ├── base.py
│ ├── capy.py
│ ├── funcaptcha.py
│ ├── geetest.py
│ ├── geetest_v4.py
│ ├── hcaptcha.py
│ ├── image.py
│ ├── keycaptcha.py
│ ├── recaptcha_v2.py
│ ├── recaptcha_v3.py
│ ├── text.py
│ └── tiktok.py
├── _misc/
│ ├── __init__.py
│ └── proxy.py
├── _service/
│ ├── __init__.py
│ ├── anti_captcha.py
│ ├── azcaptcha.py
│ ├── base.py
│ ├── captcha_guru.py
│ ├── cptch_net.py
│ ├── deathbycaptcha.py
│ ├── rucaptcha.py
│ └── twocaptcha.py
├── _solver.py
├── _solver_async.py
├── _transport/
│ ├── __init__.py
│ ├── base.py
│ └── http_transport.py
├── captcha.py
├── common.py
├── exceptions.py
└── proxy.py
SYMBOL INDEX (481 symbols across 61 files)
FILE: acceptance_tests.py
function main (line 40) | def main():
function async_main (line 68) | async def async_main():
FILE: examples/async_capy_puzzle.py
function main (line 19) | async def main():
function run (line 25) | async def run(solver):
FILE: examples/async_funcaptcha.py
function main (line 18) | async def main():
function run (line 24) | async def run(solver):
FILE: examples/async_geetest.py
function main (line 18) | async def main():
function run (line 24) | async def run(solver):
FILE: examples/async_geetest_v4.py
function main (line 19) | async def main():
function run (line 25) | async def run(solver):
FILE: examples/async_hcaptcha.py
function main (line 17) | async def main():
function run (line 23) | async def run(solver):
FILE: examples/async_image.py
function main (line 18) | async def main():
function run (line 25) | async def run(solver):
FILE: examples/async_keycaptcha.py
function main (line 19) | async def main():
function run (line 25) | async def run(solver):
FILE: examples/async_recaptcha_v2.py
function main (line 16) | async def main():
function run (line 22) | async def run(solver):
FILE: examples/async_recaptcha_v2_enterprise.py
function get_random_word (line 26) | def get_random_word(length):
function main (line 32) | async def main():
function run (line 38) | async def run(solver):
FILE: examples/async_recaptcha_v2_invisible.py
function main (line 17) | async def main():
function run (line 23) | async def run(solver):
FILE: examples/async_recaptcha_v3.py
function main (line 21) | async def main():
function run (line 27) | async def run(solver):
FILE: examples/async_text.py
function main (line 16) | async def main():
function run (line 22) | async def run(solver):
FILE: examples/capy_puzzle.py
function run (line 18) | def run(solver):
FILE: examples/funcaptcha.py
function run (line 16) | def run(solver):
FILE: examples/geetest.py
function run (line 17) | def run(solver):
FILE: examples/geetest_v4.py
function run (line 18) | def run(solver):
FILE: examples/hcaptcha.py
function run (line 16) | def run(solver):
FILE: examples/image.py
function run (line 17) | def run(solver):
FILE: examples/keycaptcha.py
function run (line 18) | def run(solver):
FILE: examples/recaptcha_v2.py
function run (line 16) | def run(solver):
FILE: examples/recaptcha_v2_enterprise.py
function get_random_word (line 27) | def get_random_word(length):
function run (line 33) | def run(solver):
FILE: examples/recaptcha_v2_invisible.py
function run (line 16) | def run(solver):
FILE: examples/recaptcha_v3.py
function run (line 20) | def run(solver):
FILE: examples/text.py
function run (line 15) | def run(solver):
FILE: tests/_helpers.py
function dict_update (line 8) | def dict_update(src_dict, upd_dict):
FILE: tests/conftest.py
function services (line 18) | def services():
function captcha_service (line 23) | def captcha_service():
function _gen_random_word (line 27) | def _gen_random_word(length):
function _get_random_value (line 32) | def _get_random_value(field):
function captcha_class (line 50) | def captcha_class(request):
function captcha_instance (line 56) | def captcha_instance(captcha_class):
FILE: tests/data/data.py
function get_http_resp_obj (line 518) | def get_http_resp_obj(ret_value, status_code=200, reason_phrase='OK', is...
FILE: tests/test_captcha_base.py
function test_captcha_class_base (line 7) | def test_captcha_class_base(captcha_class):
function test_captcha_class_get_type (line 14) | def test_captcha_class_get_type(captcha_class):
function test_captcha_class_get_solution_class (line 21) | def test_captcha_class_get_solution_class(captcha_class):
function test_captcha_class_get_optional_data (line 28) | def test_captcha_class_get_optional_data(captcha_instance):
function test_captchasolution_class_get_type (line 35) | def test_captchasolution_class_get_type(captcha_class):
function test_captchasolution_class_get_captcha_class (line 42) | def test_captchasolution_class_get_captcha_class(captcha_class):
function test_captchasolution_obj_get_string (line 49) | def test_captchasolution_obj_get_string(captcha_class):
FILE: tests/test_image_captcha.py
function image_path (line 16) | def image_path():
function image_bytes (line 21) | def image_bytes():
function image_base64 (line 26) | def image_base64(image_bytes):
function test_image_from_path (line 30) | def test_image_from_path(image_path, image_bytes):
function test_image_from_binary (line 36) | def test_image_from_binary(image_bytes):
function test_not_an_image (line 42) | def test_not_an_image():
function test_bad_image (line 48) | def test_bad_image():
FILE: tests/test_proxy.py
function test_proxy_from_string (line 9) | def test_proxy_from_string():
function test_proxy_wo_auth_from_string (line 19) | def test_proxy_wo_auth_from_string():
function test_proxy_address_only (line 29) | def test_proxy_address_only():
function test_proxy_all (line 39) | def test_proxy_all():
function test_proxy_to_string (line 49) | def test_proxy_to_string():
FILE: tests/test_service_module.py
function service_module (line 51) | def service_module(request):
function service_instance (line 56) | def service_instance(service_module):
function json_response_obj (line 61) | def json_response_obj():
function check_if_class_is_present (line 69) | def check_if_class_is_present(module, class_name, is_not=False):
function is_captcha_supported (line 78) | def is_captcha_supported(service_module, captcha_type):
function get_request (line 85) | def get_request(service_module, captcha_type, req_type='Request'):
function test_if_service_class_is_present (line 90) | def test_if_service_class_is_present(service_module):
function test_if_base_request_is_present (line 97) | def test_if_base_request_is_present(service_module, req):
function test_if_task_request_is_present (line 104) | def test_if_task_request_is_present(service_module, captcha_type):
function test_if_solution_request_is_present (line 115) | def test_if_solution_request_is_present(service_module, captcha_type):
function test_base_request_signature_of_prepare_func (line 126) | def test_base_request_signature_of_prepare_func(service_module, req):
function test_task_request_signature_of_prepare_func (line 143) | def test_task_request_signature_of_prepare_func(service_module, captcha_...
function test_solution_request_signature_of_prepare_func (line 155) | def test_solution_request_signature_of_prepare_func(service_module, capt...
function test_base_request_signature_of_parse_response_func (line 167) | def test_base_request_signature_of_parse_response_func(service_module, r...
function test_captcha_request_signature_of_parse_response_func (line 180) | def test_captcha_request_signature_of_parse_response_func(service_module...
function test_task_request_return_value_of_prepare_func (line 196) | def test_task_request_return_value_of_prepare_func(service_module, test_...
function test_task_request_return_value_of_parse_response_func (line 220) | def test_task_request_return_value_of_parse_response_func(service_module...
function test_task_request_exception_of_parse_response_func (line 245) | def test_task_request_exception_of_parse_response_func(service_module, t...
FILE: tests/test_solver.py
function captcha_solver (line 15) | def captcha_solver():
function mocked_captcha_solver (line 20) | def mocked_captcha_solver(captcha_solver, monkeypatch):
function test_solver_init (line 25) | def test_solver_init():
function test_solver_init_from_string (line 33) | def test_solver_init_from_string():
function test_solver_bad_init (line 40) | def test_solver_bad_init():
function test_solver_bad_init2 (line 45) | def test_solver_bad_init2():
function test_call_solve_func (line 51) | def test_call_solve_func(mocked_captcha_solver, captcha_instance):
FILE: unicaps/_captcha/base.py
class CaptchaType (line 13) | class CaptchaType(enum.Enum):
class BaseCaptcha (line 30) | class BaseCaptcha(ABC):
method get_type (line 34) | def get_type(cls) -> CaptchaType:
method get_solution_class (line 40) | def get_solution_class(cls) -> 'BaseCaptchaSolution':
method get_optional_data (line 45) | def get_optional_data(self, **kwargs) -> Dict:
class BaseCaptchaSolution (line 74) | class BaseCaptchaSolution(ABC):
method get_type (line 78) | def get_type(cls) -> CaptchaType:
method get_captcha_class (line 84) | def get_captcha_class(cls) -> BaseCaptcha:
method __str__ (line 92) | def __str__(self):
method as_dict (line 95) | def as_dict(self):
FILE: unicaps/_captcha/capy.py
class CapyPuzzle (line 16) | class CapyPuzzle(BaseCaptcha):
class CapyPuzzleSolution (line 26) | class CapyPuzzleSolution(BaseCaptchaSolution):
FILE: unicaps/_captcha/funcaptcha.py
class FunCaptcha (line 16) | class FunCaptcha(BaseCaptcha):
class FunCaptchaSolution (line 28) | class FunCaptchaSolution(BaseCaptchaSolution):
FILE: unicaps/_captcha/geetest.py
class GeeTest (line 16) | class GeeTest(BaseCaptcha):
class GeeTestSolution (line 27) | class GeeTestSolution(BaseCaptchaSolution):
FILE: unicaps/_captcha/geetest_v4.py
class GeeTestV4 (line 16) | class GeeTestV4(BaseCaptcha):
class GeeTestV4Solution (line 25) | class GeeTestV4Solution(BaseCaptchaSolution):
FILE: unicaps/_captcha/hcaptcha.py
class HCaptcha (line 16) | class HCaptcha(BaseCaptcha):
class HCaptchaSolution (line 27) | class HCaptchaSolution(BaseCaptchaSolution):
FILE: unicaps/_captcha/image.py
class ImageCaptcha (line 22) | class ImageCaptcha(BaseCaptcha):
method __post_init__ (line 36) | def __post_init__(self):
method get_image_bytes (line 40) | def get_image_bytes(self) -> bytes:
method get_image_base64 (line 56) | def get_image_base64(self) -> bytes:
method get_image_type (line 61) | def get_image_type(self) -> str:
class ImageCaptchaSolution (line 73) | class ImageCaptchaSolution(BaseCaptchaSolution):
FILE: unicaps/_captcha/keycaptcha.py
class KeyCaptcha (line 15) | class KeyCaptcha(BaseCaptcha):
class KeyCaptchaSolution (line 27) | class KeyCaptchaSolution(BaseCaptchaSolution):
FILE: unicaps/_captcha/recaptcha_v2.py
class RecaptchaV2 (line 16) | class RecaptchaV2(BaseCaptcha):
class RecaptchaV2Solution (line 29) | class RecaptchaV2Solution(BaseCaptchaSolution):
FILE: unicaps/_captcha/recaptcha_v3.py
class RecaptchaV3 (line 16) | class RecaptchaV3(BaseCaptcha):
class RecaptchaV3Solution (line 29) | class RecaptchaV3Solution(BaseCaptchaSolution):
FILE: unicaps/_captcha/text.py
class TextCaptcha (line 17) | class TextCaptcha(BaseCaptcha):
class TextCaptchaSolution (line 27) | class TextCaptchaSolution(BaseCaptchaSolution):
FILE: unicaps/_captcha/tiktok.py
class TikTokCaptcha (line 16) | class TikTokCaptcha(BaseCaptcha):
class TikTokCaptchaSolution (line 26) | class TikTokCaptchaSolution(BaseCaptchaSolution):
FILE: unicaps/_misc/proxy.py
function _is_ip_address (line 12) | def _is_ip_address(value):
class ProxyServerType (line 20) | class ProxyServerType(Enum):
class ProxyServer (line 30) | class ProxyServer:
method __post_init__ (line 39) | def __post_init__(self):
method __str__ (line 55) | def __str__(self):
method get_string (line 58) | def get_string(self, including_type=False):
method get_ip_address (line 70) | def get_ip_address(self):
FILE: unicaps/_service/__init__.py
class CaptchaSolvingService (line 13) | class CaptchaSolvingService(enum.Enum):
FILE: unicaps/_service/anti_captcha.py
class Service (line 27) | class Service(HTTPService):
method _post_init (line 32) | def _post_init(self):
class Request (line 46) | class Request(HTTPRequestJSON):
method prepare (line 49) | def prepare(self, **kwargs) -> dict:
method parse_response (line 61) | def parse_response(self, response) -> dict:
class GetBalanceRequest (line 109) | class GetBalanceRequest(Request):
method prepare (line 112) | def prepare(self) -> dict: # type: ignore
method parse_response (line 120) | def parse_response(self, response) -> dict:
class GetStatusRequest (line 126) | class GetStatusRequest(GetBalanceRequest):
method parse_response (line 129) | def parse_response(self, response) -> dict:
class ReportGoodRequest (line 138) | class ReportGoodRequest(Request):
method prepare (line 142) | def prepare(self, solved_captcha) -> dict: # type: ignore
class ReportBadRequest (line 150) | class ReportBadRequest(Request):
method prepare (line 154) | def prepare(self, solved_captcha) -> dict: # type: ignore
class TaskRequest (line 175) | class TaskRequest(Request):
method prepare (line 179) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
method parse_response (line 222) | def parse_response(self, response) -> dict:
class SolutionRequest (line 231) | class SolutionRequest(Request):
method prepare (line 235) | def prepare(self, task) -> dict: # type: ignore
method parse_response (line 244) | def parse_response(self, response) -> dict:
class ImageCaptchaTaskRequest (line 283) | class ImageCaptchaTaskRequest(TaskRequest):
method prepare (line 287) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class ImageCaptchaSolutionRequest (line 323) | class ImageCaptchaSolutionRequest(SolutionRequest):
class RecaptchaV2TaskRequest (line 327) | class RecaptchaV2TaskRequest(TaskRequest):
method prepare (line 331) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV2SolutionRequest (line 373) | class RecaptchaV2SolutionRequest(SolutionRequest):
class RecaptchaV3TaskRequest (line 377) | class RecaptchaV3TaskRequest(TaskRequest):
method prepare (line 381) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV3SolutionRequest (line 414) | class RecaptchaV3SolutionRequest(SolutionRequest):
class FunCaptchaTaskRequest (line 418) | class FunCaptchaTaskRequest(TaskRequest):
method prepare (line 422) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class FunCaptchaSolutionRequest (line 454) | class FunCaptchaSolutionRequest(SolutionRequest):
class GeeTestTaskRequest (line 458) | class GeeTestTaskRequest(TaskRequest):
method prepare (line 462) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class GeeTestSolutionRequest (line 492) | class GeeTestSolutionRequest(SolutionRequest):
class GeeTestV4TaskRequest (line 496) | class GeeTestV4TaskRequest(TaskRequest):
method prepare (line 500) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class GeeTestV4SolutionRequest (line 523) | class GeeTestV4SolutionRequest(SolutionRequest):
class HCaptchaTaskRequest (line 527) | class HCaptchaTaskRequest(TaskRequest):
method prepare (line 531) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class HCaptchaSolutionRequest (line 554) | class HCaptchaSolutionRequest(SolutionRequest):
FILE: unicaps/_service/azcaptcha.py
class Service (line 22) | class Service(HTTPService):
method _post_init (line 27) | def _post_init(self):
class Request (line 42) | class Request(HTTPRequestJSON):
method parse_response (line 45) | def parse_response(self, response) -> dict:
class InRequest (line 91) | class InRequest(Request):
method prepare (line 94) | def prepare(self, **kwargs) -> dict:
class ResRequest (line 117) | class ResRequest(Request):
method prepare (line 120) | def prepare(self, **kwargs) -> dict:
class GetBalanceRequest (line 142) | class GetBalanceRequest(ResRequest):
method prepare (line 145) | def prepare(self) -> dict: # type: ignore
method parse_response (line 152) | def parse_response(self, response) -> dict:
class GetStatusRequest (line 158) | class GetStatusRequest(GetBalanceRequest):
method parse_response (line 161) | def parse_response(self, response) -> dict:
class ReportGoodRequest (line 170) | class ReportGoodRequest(ResRequest):
method prepare (line 174) | def prepare(self, solved_captcha) -> dict: # type: ignore
class ReportBadRequest (line 187) | class ReportBadRequest(ResRequest):
method prepare (line 191) | def prepare(self, solved_captcha) -> dict: # type: ignore
class TaskRequest (line 204) | class TaskRequest(InRequest):
method prepare (line 208) | def prepare(self, captcha, proxy, user_agent, cookies):
method parse_response (line 232) | def parse_response(self, response) -> dict:
class SolutionRequest (line 243) | class SolutionRequest(ResRequest):
method prepare (line 247) | def prepare(self, task) -> dict: # type: ignore
method parse_response (line 256) | def parse_response(self, response) -> dict:
class ImageCaptchaTaskRequest (line 271) | class ImageCaptchaTaskRequest(TaskRequest):
method prepare (line 275) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class ImageCaptchaSolutionRequest (line 313) | class ImageCaptchaSolutionRequest(SolutionRequest):
class RecaptchaV2TaskRequest (line 317) | class RecaptchaV2TaskRequest(TaskRequest):
method prepare (line 321) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV2SolutionRequest (line 350) | class RecaptchaV2SolutionRequest(SolutionRequest):
class RecaptchaV3TaskRequest (line 354) | class RecaptchaV3TaskRequest(TaskRequest):
method prepare (line 358) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV3SolutionRequest (line 388) | class RecaptchaV3SolutionRequest(SolutionRequest):
class HCaptchaTaskRequest (line 392) | class HCaptchaTaskRequest(TaskRequest):
method prepare (line 396) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class HCaptchaSolutionRequest (line 417) | class HCaptchaSolutionRequest(SolutionRequest):
class FunCaptchaTaskRequest (line 421) | class FunCaptchaTaskRequest(TaskRequest):
method prepare (line 425) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class FunCaptchaSolutionRequest (line 454) | class FunCaptchaSolutionRequest(SolutionRequest):
FILE: unicaps/_service/base.py
class BaseService (line 22) | class BaseService(ABC):
method __init__ (line 25) | def __init__(self, api_key: str):
method _init_transport (line 33) | def _init_transport(self):
method _post_init (line 36) | def _post_init(self):
method _make_request (line 39) | def _make_request(self, request_class, *args):
method _make_request_async (line 47) | async def _make_request_async(self, request_class, *args):
method supported_captchas (line 56) | def supported_captchas(self) -> Tuple[CaptchaType, ...]:
method settings (line 66) | def settings(self) -> Dict[CaptchaType, 'Settings']:
method solve_captcha (line 71) | def solve_captcha(self, captcha: BaseCaptcha, proxy: Optional[ProxySer...
method solve_captcha_async (line 84) | async def solve_captcha_async(self, captcha: BaseCaptcha, proxy: Optio...
method create_task (line 97) | def create_task(self, captcha: BaseCaptcha, proxy: Optional[ProxyServe...
method create_task_async (line 114) | async def create_task_async(self, captcha: BaseCaptcha, proxy: Optiona...
method get_task_result (line 131) | def get_task_result(self, task: 'CaptchaTask') -> Tuple[BaseCaptchaSol...
method get_task_result_async (line 143) | async def get_task_result_async(self, task: 'CaptchaTask') -> Tuple[Ba...
method wait_for_solution (line 155) | def wait_for_solution(self, task) -> Tuple[BaseCaptchaSolution, Option...
method wait_for_solution_async (line 173) | async def wait_for_solution_async(self, task) -> Tuple[BaseCaptchaSolu...
method get_balance (line 192) | def get_balance(self):
method get_balance_async (line 200) | async def get_balance_async(self):
method get_status (line 209) | def get_status(self) -> bool:
method get_status_async (line 214) | async def get_status_async(self) -> bool:
method report_good (line 219) | def report_good(self, solved_captcha: 'SolvedCaptcha', raise_exc: bool...
method report_good_async (line 230) | async def report_good_async(self, solved_captcha: 'SolvedCaptcha',
method report_bad (line 242) | def report_bad(self, solved_captcha: 'SolvedCaptcha', raise_exc: bool ...
method report_bad_async (line 253) | async def report_bad_async(self, solved_captcha: 'SolvedCaptcha',
method close (line 266) | def close(self):
method close_async (line 270) | async def close_async(self):
class HTTPService (line 274) | class HTTPService(BaseService):
method _init_transport (line 277) | def _init_transport(self):
method close (line 280) | def close(self):
method close_async (line 284) | async def close_async(self):
class Settings (line 290) | class Settings:
class CaptchaTask (line 298) | class CaptchaTask:
method __init__ (line 301) | def __init__(self, service, captcha: BaseCaptcha, task_id: str, extra:...
method task_id (line 309) | def task_id(self) -> str:
method captcha (line 314) | def captcha(self) -> BaseCaptcha:
method extra (line 319) | def extra(self) -> Dict:
method get_result (line 323) | def get_result(self) -> Optional[BaseCaptchaSolution]:
method is_done (line 329) | def is_done(self) -> bool:
method wait (line 333) | def wait(self) -> BaseCaptchaSolution:
class AsyncCaptchaTask (line 338) | class AsyncCaptchaTask(CaptchaTask):
method get_result (line 341) | async def get_result(self) -> Optional[BaseCaptchaSolution]: # type: ...
method wait (line 347) | async def wait(self) -> BaseCaptchaSolution: # type: ignore
class SolvedCaptcha (line 352) | class SolvedCaptcha:
method __init__ (line 355) | def __init__(self, task: CaptchaTask, solution: BaseCaptchaSolution, s...
method captcha_id (line 370) | def captcha_id(self) -> str:
method task (line 375) | def task(self) -> CaptchaTask:
method solution (line 380) | def solution(self) -> BaseCaptchaSolution:
method start_time (line 385) | def start_time(self) -> datetime:
method end_time (line 390) | def end_time(self) -> datetime:
method cost (line 395) | def cost(self) -> Optional[float]:
method cookies (line 400) | def cookies(self) -> dict:
method extra (line 405) | def extra(self) -> dict:
method report_good (line 409) | def report_good(self, raise_exc: bool = False) -> bool:
method report_bad (line 414) | def report_bad(self, raise_exc: bool = False) -> bool:
class AsyncSolvedCaptcha (line 420) | class AsyncSolvedCaptcha(SolvedCaptcha):
method report_good (line 423) | async def report_good(self, raise_exc: bool = False) -> bool: # type:...
method report_bad (line 428) | async def report_bad(self, raise_exc: bool = False) -> bool: # type: ...
FILE: unicaps/_service/captcha_guru.py
class Service (line 27) | class Service(Service2Captcha):
function _decorator (line 33) | def _decorator(cls):
FILE: unicaps/_service/cptch_net.py
class Service (line 20) | class Service(HTTPService):
method _post_init (line 25) | def _post_init(self):
class Request (line 40) | class Request(HTTPRequestJSON):
method parse_response (line 43) | def parse_response(self, response) -> dict:
class InRequest (line 87) | class InRequest(Request):
method prepare (line 90) | def prepare(self, **kwargs) -> dict:
class ResRequest (line 113) | class ResRequest(Request):
method prepare (line 116) | def prepare(self, **kwargs) -> dict:
class GetBalanceRequest (line 138) | class GetBalanceRequest(ResRequest):
method prepare (line 141) | def prepare(self) -> dict: # type: ignore
method parse_response (line 148) | def parse_response(self, response) -> dict:
class GetStatusRequest (line 154) | class GetStatusRequest(GetBalanceRequest):
method parse_response (line 157) | def parse_response(self, response) -> dict:
class ReportGoodRequest (line 166) | class ReportGoodRequest(ResRequest):
method prepare (line 170) | def prepare(self, solved_captcha) -> dict: # type: ignore
class ReportBadRequest (line 183) | class ReportBadRequest(ResRequest):
method prepare (line 187) | def prepare(self, solved_captcha) -> dict: # type: ignore
class TaskRequest (line 200) | class TaskRequest(InRequest):
method parse_response (line 203) | def parse_response(self, response) -> dict:
class SolutionRequest (line 214) | class SolutionRequest(ResRequest):
method prepare (line 218) | def prepare(self, task) -> dict: # type: ignore
method parse_response (line 227) | def parse_response(self, response) -> dict:
class ImageCaptchaTaskRequest (line 245) | class ImageCaptchaTaskRequest(TaskRequest):
method prepare (line 249) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class ImageCaptchaSolutionRequest (line 287) | class ImageCaptchaSolutionRequest(SolutionRequest):
class RecaptchaV2TaskRequest (line 291) | class RecaptchaV2TaskRequest(TaskRequest):
method prepare (line 295) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV2SolutionRequest (line 317) | class RecaptchaV2SolutionRequest(SolutionRequest):
class RecaptchaV3TaskRequest (line 321) | class RecaptchaV3TaskRequest(TaskRequest):
method prepare (line 325) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV3SolutionRequest (line 355) | class RecaptchaV3SolutionRequest(SolutionRequest):
FILE: unicaps/_service/deathbycaptcha.py
class Service (line 23) | class Service(HTTPService):
method _post_init (line 28) | def _post_init(self):
class Request (line 44) | class Request(HTTPRequestJSON):
method prepare (line 47) | def prepare(self, **kwargs) -> dict:
method parse_response (line 64) | def parse_response(self, response) -> dict:
class PostRequest (line 105) | class PostRequest(Request):
method prepare (line 108) | def prepare(self, **kwargs) -> dict:
class GetRequest (line 113) | class GetRequest(Request):
method prepare (line 116) | def prepare(self, **kwargs) -> dict:
class GetBalanceRequest (line 121) | class GetBalanceRequest(GetRequest):
method prepare (line 124) | def prepare(self) -> dict:
method parse_response (line 128) | def parse_response(self, response) -> dict:
class GetStatusRequest (line 135) | class GetStatusRequest(GetRequest):
method prepare (line 138) | def prepare(self):
method parse_response (line 142) | def parse_response(self, response) -> dict:
class ReportGoodRequest (line 153) | class ReportGoodRequest(PostRequest):
method prepare (line 157) | def prepare(self, solved_captcha) -> dict: # type: ignore
class ReportBadRequest (line 164) | class ReportBadRequest(PostRequest):
method prepare (line 168) | def prepare(self, solved_captcha) -> dict: # type: ignore
class TaskRequest (line 176) | class TaskRequest(PostRequest):
method prepare (line 180) | def prepare(self, captcha, proxy, user_agent, cookies):
method parse_response (line 193) | def parse_response(self, response) -> dict:
class SolutionRequest (line 211) | class SolutionRequest(GetRequest):
method prepare (line 215) | def prepare(self, task) -> dict: # type: ignore
method parse_response (line 219) | def parse_response(self, response) -> dict:
class ImageCaptchaTaskRequest (line 245) | class ImageCaptchaTaskRequest(TaskRequest):
method prepare (line 249) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class ImageCaptchaSolutionRequest (line 266) | class ImageCaptchaSolutionRequest(SolutionRequest):
class RecaptchaV2TaskRequest (line 270) | class RecaptchaV2TaskRequest(TaskRequest):
method prepare (line 274) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV2SolutionRequest (line 304) | class RecaptchaV2SolutionRequest(SolutionRequest):
class RecaptchaV3TaskRequest (line 308) | class RecaptchaV3TaskRequest(TaskRequest):
method prepare (line 312) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV3SolutionRequest (line 344) | class RecaptchaV3SolutionRequest(SolutionRequest):
class FunCaptchaTaskRequest (line 348) | class FunCaptchaTaskRequest(TaskRequest):
method prepare (line 352) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class FunCaptchaSolutionRequest (line 377) | class FunCaptchaSolutionRequest(SolutionRequest):
class HCaptchaTaskRequest (line 381) | class HCaptchaTaskRequest(TaskRequest):
method prepare (line 385) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class HCaptchaSolutionRequest (line 410) | class HCaptchaSolutionRequest(SolutionRequest):
function _dumps (line 414) | def _dumps(data, proxy):
FILE: unicaps/_service/rucaptcha.py
class Service (line 40) | class Service(Service2Captcha):
FILE: unicaps/_service/twocaptcha.py
class Service (line 29) | class Service(HTTPService):
method _post_init (line 34) | def _post_init(self):
class Request (line 51) | class Request(HTTPRequestJSON):
method parse_response (line 54) | def parse_response(self, response) -> dict:
class InRequest (line 100) | class InRequest(Request):
method prepare (line 103) | def prepare(self, **kwargs) -> dict:
class ResRequest (line 121) | class ResRequest(Request):
method prepare (line 124) | def prepare(self, **kwargs) -> dict:
class GetBalanceRequest (line 141) | class GetBalanceRequest(ResRequest):
method prepare (line 144) | def prepare(self) -> dict:
method parse_response (line 151) | def parse_response(self, response) -> dict:
class GetStatusRequest (line 157) | class GetStatusRequest(GetBalanceRequest):
method parse_response (line 160) | def parse_response(self, response) -> dict:
class ReportGoodRequest (line 169) | class ReportGoodRequest(ResRequest):
method prepare (line 173) | def prepare(self, solved_captcha) -> dict: # type: ignore
class ReportBadRequest (line 186) | class ReportBadRequest(ResRequest):
method prepare (line 190) | def prepare(self, solved_captcha) -> dict: # type: ignore
class TaskRequest (line 203) | class TaskRequest(InRequest):
method prepare (line 207) | def prepare(self, captcha, proxy, user_agent, cookies):
method parse_response (line 233) | def parse_response(self, response) -> dict:
class SolutionRequest (line 244) | class SolutionRequest(ResRequest):
method prepare (line 248) | def prepare(self, task) -> dict: # type: ignore
method parse_response (line 257) | def parse_response(self, response) -> dict:
class ImageCaptchaTaskRequest (line 301) | class ImageCaptchaTaskRequest(TaskRequest):
method prepare (line 305) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class ImageCaptchaSolutionRequest (line 343) | class ImageCaptchaSolutionRequest(SolutionRequest):
class RecaptchaV2TaskRequest (line 347) | class RecaptchaV2TaskRequest(TaskRequest):
method prepare (line 351) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV2SolutionRequest (line 384) | class RecaptchaV2SolutionRequest(SolutionRequest):
class RecaptchaV3TaskRequest (line 388) | class RecaptchaV3TaskRequest(TaskRequest):
method prepare (line 392) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class RecaptchaV3SolutionRequest (line 426) | class RecaptchaV3SolutionRequest(SolutionRequest):
class TextCaptchaTaskRequest (line 430) | class TextCaptchaTaskRequest(TaskRequest):
method prepare (line 434) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class TextCaptchaSolutionRequest (line 463) | class TextCaptchaSolutionRequest(SolutionRequest):
class FunCaptchaTaskRequest (line 467) | class FunCaptchaTaskRequest(TaskRequest):
method prepare (line 471) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class FunCaptchaSolutionRequest (line 501) | class FunCaptchaSolutionRequest(SolutionRequest):
class KeyCaptchaTaskRequest (line 505) | class KeyCaptchaTaskRequest(TaskRequest):
method prepare (line 509) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class KeyCaptchaSolutionRequest (line 533) | class KeyCaptchaSolutionRequest(SolutionRequest):
class GeeTestTaskRequest (line 537) | class GeeTestTaskRequest(TaskRequest):
method prepare (line 541) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class GeeTestSolutionRequest (line 570) | class GeeTestSolutionRequest(SolutionRequest):
class HCaptchaTaskRequest (line 574) | class HCaptchaTaskRequest(TaskRequest):
method prepare (line 578) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class HCaptchaSolutionRequest (line 607) | class HCaptchaSolutionRequest(SolutionRequest):
class CapyPuzzleTaskRequest (line 611) | class CapyPuzzleTaskRequest(TaskRequest):
method prepare (line 615) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class CapyPuzzleSolutionRequest (line 643) | class CapyPuzzleSolutionRequest(SolutionRequest):
class TikTokCaptchaTaskRequest (line 647) | class TikTokCaptchaTaskRequest(TaskRequest):
method prepare (line 651) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class TikTokCaptchaSolutionRequest (line 692) | class TikTokCaptchaSolutionRequest(SolutionRequest):
class GeeTestV4TaskRequest (line 696) | class GeeTestV4TaskRequest(TaskRequest):
method prepare (line 700) | def prepare(self, captcha, proxy, user_agent, cookies) -> dict: # typ...
class GeeTestV4SolutionRequest (line 721) | class GeeTestV4SolutionRequest(SolutionRequest):
FILE: unicaps/_solver.py
class CaptchaSolver (line 18) | class CaptchaSolver:
method __init__ (line 25) | def __init__(self, service_name: Union[CaptchaSolvingService, str], ap...
method _solve_captcha (line 47) | def _solve_captcha(self, captcha_class, *args, **kwargs):
method solve_image_captcha (line 59) | def solve_image_captcha(self,
method solve_text_captcha (line 80) | def solve_text_captcha(self, text: str, **kwargs) -> SolvedCaptcha:
method solve_recaptcha_v2 (line 91) | def solve_recaptcha_v2(self, site_key: str, page_url: str, **kwargs) -...
method solve_recaptcha_v3 (line 108) | def solve_recaptcha_v3(self, site_key: str, page_url: str, **kwargs) -...
method solve_hcaptcha (line 125) | def solve_hcaptcha(self, site_key: str, page_url: str, **kwargs) -> So...
method solve_funcaptcha (line 140) | def solve_funcaptcha(self, public_key: str, page_url: str, **kwargs) -...
method solve_keycaptcha (line 156) | def solve_keycaptcha(self, page_url: str, user_id: str, session_id: st...
method solve_geetest (line 172) | def solve_geetest(self, page_url: str, gt_key: str, challenge: str,
method solve_geetest_v4 (line 185) | def solve_geetest_v4(self, page_url: str, captcha_id: str, **kwargs) -...
method solve_capy_puzzle (line 195) | def solve_capy_puzzle(self, site_key: str, page_url: str, **kwargs) ->...
method solve_tiktok (line 209) | def solve_tiktok(self, page_url: str, **kwargs) -> SolvedCaptcha:
method create_task (line 223) | def create_task(self, captcha: BaseCaptcha) -> CaptchaTask:
method get_balance (line 232) | def get_balance(self) -> float:
method get_status (line 240) | def get_status(self) -> bool:
method close (line 248) | def close(self) -> None:
method __enter__ (line 252) | def __enter__(self):
method __exit__ (line 255) | def __exit__(self, exc_type, exc_value, traceback):
FILE: unicaps/_solver_async.py
class AsyncCaptchaSolver (line 17) | class AsyncCaptchaSolver(CaptchaSolver):
method _solve_captcha_async (line 24) | async def _solve_captcha_async(self, captcha_class, *args, **kwargs):
method solve_image_captcha (line 36) | async def solve_image_captcha(self, # type: ignore
method solve_text_captcha (line 58) | async def solve_text_captcha(self, text: str, **kwargs) -> AsyncSolved...
method solve_recaptcha_v2 (line 69) | async def solve_recaptcha_v2(self, site_key: str, page_url: str, # ty...
method solve_recaptcha_v3 (line 87) | async def solve_recaptcha_v3(self, site_key: str, page_url: str, # ty...
method solve_hcaptcha (line 105) | async def solve_hcaptcha(self, site_key: str, page_url: str, # type: ...
method solve_funcaptcha (line 121) | async def solve_funcaptcha(self, public_key: str, page_url: str, # ty...
method solve_keycaptcha (line 138) | async def solve_keycaptcha(self, page_url: str, user_id: str, session_...
method solve_geetest (line 154) | async def solve_geetest(self, page_url: str, gt_key: str, challenge: s...
method solve_geetest_v4 (line 167) | async def solve_geetest_v4(self, page_url: str, captcha_id: str, # ty...
method solve_capy_puzzle (line 178) | async def solve_capy_puzzle(self, site_key: str, page_url: str, # typ...
method solve_tiktok (line 193) | async def solve_tiktok(self, page_url: str, **kwargs) -> AsyncSolvedCa...
method create_task (line 207) | async def create_task(self, captcha: BaseCaptcha) -> AsyncCaptchaTask:...
method get_balance (line 216) | async def get_balance(self) -> float: # type: ignore
method get_status (line 224) | async def get_status(self) -> bool: # type: ignore
method close (line 232) | async def close(self) -> None: # type: ignore
method __aenter__ (line 236) | async def __aenter__(self):
method __aexit__ (line 239) | async def __aexit__(self, exc_type, exc_value, traceback):
FILE: unicaps/_transport/base.py
class BaseRequest (line 10) | class BaseRequest(ABC):
method __init__ (line 13) | def __init__(self, service):
method prepare (line 20) | def prepare(self, **kwargs) -> dict:
method parse_response (line 26) | def parse_response(self, response: Any) -> dict:
method process_response (line 32) | def process_response(self, response: Any) -> dict:
class BaseTransport (line 39) | class BaseTransport(ABC): # pylint: disable=too-few-public-methods
method __init__ (line 42) | def __init__(self, settings: Optional[dict] = None):
method _make_request (line 46) | def _make_request(self, request_data: dict) -> Any:
method _make_request_async (line 50) | async def _make_request_async(self, request_data: dict) -> Any:
method make_request (line 53) | def make_request(self, request: BaseRequest, *args) -> dict:
method make_request_async (line 58) | async def make_request_async(self, request: BaseRequest, *args) -> dict:
method close (line 64) | def close(self):
method close_async (line 68) | async def close_async(self):
FILE: unicaps/_transport/http_transport.py
class StandardHTTPTransport (line 21) | class StandardHTTPTransport(BaseTransport): # pylint: disable=too-few-p...
method __init__ (line 24) | def __init__(self, settings: Optional[Dict] = None):
method _make_request (line 42) | def _make_request(self, request_data: Dict) -> httpx.Response:
method _make_request_async (line 61) | async def _make_request_async(self, request_data: Dict) -> httpx.Respo...
method close (line 80) | def close(self):
method close_async (line 84) | async def close_async(self):
class HTTPRequestJSON (line 89) | class HTTPRequestJSON(BaseRequest):
method prepare (line 92) | def prepare(self, **kwargs) -> Dict:
method parse_response (line 101) | def parse_response(self, response: httpx.Response) -> Dict:
FILE: unicaps/common.py
class CaptchaAlphabet (line 9) | class CaptchaAlphabet(enum.Enum):
class CaptchaCharType (line 16) | class CaptchaCharType(enum.Enum):
class WorkerLanguage (line 25) | class WorkerLanguage(enum.Enum):
FILE: unicaps/exceptions.py
class UnicapsException (line 10) | class UnicapsException(Exception):
class SolutionNotReadyYet (line 14) | class SolutionNotReadyYet(UnicapsException):
class ServiceError (line 18) | class ServiceError(UnicapsException):
class CaptchaError (line 22) | class CaptchaError(UnicapsException):
class NetworkError (line 26) | class NetworkError(UnicapsException):
class ProxyError (line 33) | class ProxyError(UnicapsException):
class AccessDeniedError (line 39) | class AccessDeniedError(ServiceError):
class LowBalanceError (line 47) | class LowBalanceError(ServiceError):
class ServiceTooBusy (line 53) | class ServiceTooBusy(ServiceError):
class SolutionWaitTimeout (line 59) | class SolutionWaitTimeout(ServiceError):
class TooManyRequestsError (line 65) | class TooManyRequestsError(ServiceError):
class MalformedRequestError (line 71) | class MalformedRequestError(ServiceError):
class BadInputDataError (line 77) | class BadInputDataError(CaptchaError):
class UnableToSolveError (line 86) | class UnableToSolveError(CaptchaError):
Condensed preview — 81 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (324K chars).
[
{
"path": ".github/workflows/python-package.yml",
"chars": 1439,
"preview": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more inform"
},
{
"path": ".github/workflows/python-publish.yml",
"chars": 1076,
"preview": "# This workflow will upload a Python Package using Twine when a release is created\n# For more information see: https://h"
},
{
"path": ".gitignore",
"chars": 1822,
"preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
},
{
"path": "LICENSE",
"chars": 11346,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "README.md",
"chars": 24165,
"preview": "<p align=\"center\">\n <img src=\"https://i.imgur.com/8aQf6On.png\" />\n</p>\n\n# Unicaps\n[ as fh:\n long_description = fh.read()\n\nsetuptools.set"
},
{
"path": "tests/_helpers.py",
"chars": 357,
"preview": "\"\"\"\nHelpers\n\"\"\"\n\nimport collections.abc\n\n\ndef dict_update(src_dict, upd_dict):\n \"\"\" Updates nested dict recursively \""
},
{
"path": "tests/conftest.py",
"chars": 1546,
"preview": "# -*- coding: UTF-8 -*-\n\nimport enum\nimport importlib\nimport os.path\nimport random\nimport string\nimport sys\n\nfrom pytest"
},
{
"path": "tests/data/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "tests/data/data.py",
"chars": 40605,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nTest data\n\"\"\"\n\nimport base64\nimport json\nimport os.path\nimport pathlib\nfrom random import ch"
},
{
"path": "tests/test_captcha_base.py",
"chars": 1975,
"preview": "# -*- coding: UTF-8 -*-\n\nfrom unicaps._captcha import CaptchaType\nfrom unicaps._captcha.base import BaseCaptcha, BaseCap"
},
{
"path": "tests/test_image_captcha.py",
"chars": 1176,
"preview": "# -*- coding: UTF-8 -*-\n\nimport base64\nimport os.path\nimport pathlib\n\nimport pytest\nfrom unicaps.captcha import ImageCap"
},
{
"path": "tests/test_proxy.py",
"chars": 1396,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nProxy tests\n\"\"\"\n\nfrom unicaps.proxy import ProxyServer, ProxyServerType\n\n\ndef test_proxy_fro"
},
{
"path": "tests/test_service_module.py",
"chars": 11050,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nService module tests\n\"\"\"\n\nimport importlib\nimport inspect\nfrom copy import deepcopy\nfrom uni"
},
{
"path": "tests/test_solver.py",
"chars": 1932,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nCaptchaSolver tests\n\"\"\"\nfrom unittest.mock import Mock\n\nimport pytest\nfrom unicaps import Ca"
},
{
"path": "unicaps/__init__.py",
"chars": 311,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nUnicaps package\n~~~~~~~~~~~~~~~\n\"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom ._sol"
},
{
"path": "unicaps/__version__.py",
"chars": 276,
"preview": "\"\"\" Version info \"\"\"\n\n__title__ = 'unicaps'\n__description__ = 'Python CAPTCHA solving for Humans.'\n__url__ = 'https://gi"
},
{
"path": "unicaps/_captcha/__init__.py",
"chars": 716,
"preview": "# -*- coding: UTF-8 -*-\n\n\"\"\" CAPTCHAs \"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom .image import ImageCaptcha\n"
},
{
"path": "unicaps/_captcha/base.py",
"chars": 2639,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nBase CAPTCHA stuff\n\"\"\"\n\nimport enum\nimport importlib\nfrom abc import ABC\nfrom dataclasses im"
},
{
"path": "unicaps/_captcha/capy.py",
"chars": 557,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nCapy Puzzle\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce"
},
{
"path": "unicaps/_captcha/funcaptcha.py",
"chars": 562,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nFunCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_"
},
{
"path": "unicaps/_captcha/geetest.py",
"chars": 544,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nGeeTest CAPTCHA\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enf"
},
{
"path": "unicaps/_captcha/geetest_v4.py",
"chars": 553,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nGeeTest v4 CAPTCHA\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom "
},
{
"path": "unicaps/_captcha/hcaptcha.py",
"chars": 516,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nhCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_ty"
},
{
"path": "unicaps/_captcha/image.py",
"chars": 2090,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nImage CAPTCHA\n\"\"\"\n\nimport base64\nimport imghdr\nimport io\nimport pathlib\nfrom dataclasses imp"
},
{
"path": "unicaps/_captcha/keycaptcha.py",
"chars": 484,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nKeyCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\n\nfrom enforce_typing import enforce_types "
},
{
"path": "unicaps/_captcha/recaptcha_v2.py",
"chars": 620,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nGoogle reCAPTCHA v2\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom"
},
{
"path": "unicaps/_captcha/recaptcha_v3.py",
"chars": 627,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nGoogle reCAPTCHA v3\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom"
},
{
"path": "unicaps/_captcha/text.py",
"chars": 589,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nText CAPTCHA\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforc"
},
{
"path": "unicaps/_captcha/tiktok.py",
"chars": 519,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nTikTokCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enfor"
},
{
"path": "unicaps/_misc/__init__.py",
"chars": 52,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nMiscellaneous stuff\n\"\"\"\n"
},
{
"path": "unicaps/_misc/proxy.py",
"chars": 1946,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nProxy Server representation\n\"\"\"\nimport socket\n\nfrom dataclasses import dataclass\nfrom enum i"
},
{
"path": "unicaps/_service/__init__.py",
"chars": 961,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nCertain services related stuff\n\"\"\"\n\nimport enum\n# pylint: disable=import-self\nfrom . import "
},
{
"path": "unicaps/_service/anti_captcha.py",
"chars": 18499,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nanti-captcha.com service\n\"\"\"\n\nimport json\n\nfrom .base import HTTPService\nfrom .._transport.h"
},
{
"path": "unicaps/_service/azcaptcha.py",
"chars": 13742,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nazcaptcha.com service\n\"\"\"\nfrom .base import HTTPService\nfrom .._transport.http_transport imp"
},
{
"path": "unicaps/_service/base.py",
"chars": 14622,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nBase service stuff\n\"\"\"\n\nimport asyncio\nimport time\nfrom abc import ABC, abstractmethod\nfrom "
},
{
"path": "unicaps/_service/captcha_guru.py",
"chars": 2333,
"preview": "\"\"\"\ncap.guru service\n\"\"\"\n\n# pylint: disable=unused-import\nfrom .twocaptcha import (\n Service as Service2Captcha, GetB"
},
{
"path": "unicaps/_service/cptch_net.py",
"chars": 11027,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\ncptch.net service\n\"\"\"\nfrom .base import HTTPService\nfrom .._transport.http_transport import "
},
{
"path": "unicaps/_service/deathbycaptcha.py",
"chars": 12377,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\ndeathbycaptcha.com service\n\"\"\"\nimport json\n\nfrom .base import HTTPService\nfrom .._transport."
},
{
"path": "unicaps/_service/rucaptcha.py",
"chars": 1698,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nrucaptcha.com service\n\"\"\"\n\n# pylint: disable=unused-import\nfrom .twocaptcha import (\n Ser"
},
{
"path": "unicaps/_service/twocaptcha.py",
"chars": 21810,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\n2captcha.com service\n\"\"\"\n\nfrom .base import HTTPService\nfrom .._transport.http_transport imp"
},
{
"path": "unicaps/_solver.py",
"chars": 11458,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nCaptchaSolver class\n\"\"\"\nimport io\nimport pathlib\nfrom typing import Union\n\nfrom .captcha imp"
},
{
"path": "unicaps/_solver_async.py",
"chars": 11515,
"preview": "\"\"\"\nAsyncCaptchaSolver class\n\"\"\"\nimport io\nimport pathlib\nfrom typing import Union\n\nfrom .captcha import (\n ImageCapt"
},
{
"path": "unicaps/_transport/__init__.py",
"chars": 178,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nTransport related stuff\n\"\"\"\n\nfrom .http_transport import StandardHTTPTransport, HTTPRequestJ"
},
{
"path": "unicaps/_transport/base.py",
"chars": 2108,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nBase transport stuff\n\"\"\"\n\nfrom abc import ABC, abstractmethod\nfrom typing import Optional, A"
},
{
"path": "unicaps/_transport/http_transport.py",
"chars": 3441,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nTransport and requests for HTTP protocol\n\"\"\"\n\nfrom json.decoder import JSONDecodeError\nfrom "
},
{
"path": "unicaps/captcha.py",
"chars": 542,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nSupported CAPTCHAs\n~~~~~~~~~~~~~~~~~~\n\"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom"
},
{
"path": "unicaps/common.py",
"chars": 1498,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nCAPTCHA common stuff\n\"\"\"\n\nimport enum\n\n\nclass CaptchaAlphabet(enum.Enum):\n \"\"\" Alphabet u"
},
{
"path": "unicaps/exceptions.py",
"chars": 1437,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nunicaps.exceptions\n~~~~~~~~~~~~~~~~~~~\n\nThis module contains the set of Unicaps' exceptions."
},
{
"path": "unicaps/proxy.py",
"chars": 182,
"preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nProxy\n\"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom ._misc.proxy import ProxyServer"
}
]
About this extraction
This page contains the full source code of the sergey-scat/unicaps GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 81 files (298.6 KB), approximately 73.8k tokens, and a symbol index with 481 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.