Full Code of sergey-scat/unicaps for AI

master bd24c99ec4e9 cached
81 files
298.6 KB
73.8k tokens
481 symbols
1 requests
Download .txt
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
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/unicaps.png)](https://pypi.python.org/pypi/unicaps/)
[![PyPI version](https://img.shields.io/pypi/v/unicaps)](https://pypi.python.org/pypi/unicaps/)
[![PyPI status](https://img.shields.io/pypi/status/unicaps.png)](https://pypi.python.org/pypi/unicaps/)
[![CodeFactor](https://www.codefactor.io/repository/github/sergey-scat/unicaps/badge)](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):
 
Download .txt
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
Download .txt
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[![PyPI pyversions](https://img.shiel"
  },
  {
    "path": "acceptance_tests.py",
    "chars": 3042,
    "preview": "\"\"\"\nAcceptance tests\n\"\"\"\n\nimport asyncio\nimport logging\nimport os\nfrom importlib import import_module\n\nfrom unicaps impo"
  },
  {
    "path": "examples/README.MD",
    "chars": 789,
    "preview": "\n# Real-life examples\nHere are some real-life examples of solving captchas, both with a synchronous client and asynchron"
  },
  {
    "path": "examples/async_capy_puzzle.py",
    "chars": 2589,
    "preview": "\"\"\"\nCapy Puzzle CAPTCHA solving example\n\"\"\"\n\nimport asyncio\nimport os\nfrom urllib.parse import urlparse, parse_qs\n\nimpor"
  },
  {
    "path": "examples/async_funcaptcha.py",
    "chars": 2286,
    "preview": "\"\"\"\nFunCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\n\nimport httpx\nfrom lxml import html  # type: igno"
  },
  {
    "path": "examples/async_geetest.py",
    "chars": 2051,
    "preview": "\"\"\"\nGeeTest solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport time\n\nimport httpx\nfrom unicaps import AsyncCaptchaSolv"
  },
  {
    "path": "examples/async_geetest_v4.py",
    "chars": 2592,
    "preview": "\"\"\"\nGeeTest v4 solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfr"
  },
  {
    "path": "examples/async_hcaptcha.py",
    "chars": 2278,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nhCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml import html  "
  },
  {
    "path": "examples/async_image.py",
    "chars": 2855,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nImage CAPTCHA solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml import h"
  },
  {
    "path": "examples/async_keycaptcha.py",
    "chars": 2763,
    "preview": "\"\"\"\nKeyCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfr"
  },
  {
    "path": "examples/async_recaptcha_v2.py",
    "chars": 2124,
    "preview": "\"\"\"\nreCAPTCHA v2 async solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore"
  },
  {
    "path": "examples/async_recaptcha_v2_enterprise.py",
    "chars": 3209,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 Enterprise solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport random\nimport s"
  },
  {
    "path": "examples/async_recaptcha_v2_invisible.py",
    "chars": 2230,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 invisible solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml"
  },
  {
    "path": "examples/async_recaptcha_v3.py",
    "chars": 2576,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v3 solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\nfrom pprint import ppri"
  },
  {
    "path": "examples/async_text.py",
    "chars": 1367,
    "preview": "\"\"\"\nTextCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingSer"
  },
  {
    "path": "examples/capy_puzzle.py",
    "chars": 2388,
    "preview": "\"\"\"\nCapy Puzzle CAPTCHA solving example\n\"\"\"\n\nimport os\nfrom urllib.parse import urlparse, parse_qs\n\nimport httpx\nfrom lx"
  },
  {
    "path": "examples/funcaptcha.py",
    "chars": 2117,
    "preview": "\"\"\"\nFunCaptcha solving example\n\"\"\"\nimport os\nimport re\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps "
  },
  {
    "path": "examples/geetest.py",
    "chars": 1883,
    "preview": "\"\"\"\nGeeTest solving example\n\"\"\"\n\nimport os\nimport time\n\nimport httpx\nfrom unicaps import CaptchaSolver, CaptchaSolvingSe"
  },
  {
    "path": "examples/geetest_v4.py",
    "chars": 2425,
    "preview": "\"\"\"\nGeeTest solving example\n\"\"\"\n\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfrom lxml import htm"
  },
  {
    "path": "examples/hcaptcha.py",
    "chars": 2110,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nhCaptcha solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\n"
  },
  {
    "path": "examples/image.py",
    "chars": 2796,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nImage CAPTCHA solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  # type: ig"
  },
  {
    "path": "examples/keycaptcha.py",
    "chars": 2589,
    "preview": "\"\"\"\nKeyCaptcha solving example\n\"\"\"\n\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfrom lxml import "
  },
  {
    "path": "examples/recaptcha_v2.py",
    "chars": 1984,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  # type: ign"
  },
  {
    "path": "examples/recaptcha_v2_enterprise.py",
    "chars": 3420,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 Enterprise solving example\n\"\"\"\n\nimport os\nimport random\nimport string\nfrom urll"
  },
  {
    "path": "examples/recaptcha_v2_invisible.py",
    "chars": 2062,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 invisible solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  #"
  },
  {
    "path": "examples/recaptcha_v3.py",
    "chars": 2428,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v3 solving example\n\"\"\"\n\nimport os\nimport re\nfrom pprint import pprint\n\nimport http"
  },
  {
    "path": "examples/requirements.txt",
    "chars": 17,
    "preview": "httpx[http2]\nlxml"
  },
  {
    "path": "examples/run_all.py",
    "chars": 1312,
    "preview": "# -*- coding: UTF-8 -*-\n\"\"\"\nAll examples runner\n\"\"\"\n\nfrom importlib import import_module\nimport logging\nimport os\n\nfrom "
  },
  {
    "path": "examples/text.py",
    "chars": 1215,
    "preview": "\"\"\"\nTextCaptcha solving example\n\"\"\"\n\nimport os\n\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # "
  },
  {
    "path": "requirements-dev.txt",
    "chars": 39,
    "preview": "flake8>=3.8.3\npytest>=6.0.1\ntox>=3.17.1"
  },
  {
    "path": "requirements.txt",
    "chars": 29,
    "preview": "httpx>=0.22.0\nenforce-typing\n"
  },
  {
    "path": "setup.cfg",
    "chars": 179,
    "preview": "[pycodestyle]\nignore = E701\nmax-line-length = 100\n\n[pylint.checkers]\nmax-line-length = 100\nmax-args = 8\nmax-attributes ="
  },
  {
    "path": "setup.py",
    "chars": 1088,
    "preview": "import setuptools\n\nwith open(\"README.md\", \"r\", encoding=\"utf-8\") 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.

Copied to clipboard!