[
  {
    "path": ".github/workflows/python-package.yml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a variety of Python versions\n# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions\n\nname: Python package\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n    strategy:\n      fail-fast: false\n      matrix:\n        python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python ${{ matrix.python-version }}\n      uses: actions/setup-python@v2\n      with:\n        python-version: ${{ matrix.python-version }}\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip setuptools wheel\n        if [ -f requirements-dev.txt ]; then pip install -r requirements-dev.txt; fi\n        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi\n    - name: Lint with flake8\n      run: |\n        # stop the build if there are Python syntax errors or undefined names\n        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics\n        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide\n        flake8 . --count --exit-zero --max-complexity=12 --max-line-length=100 --statistics --per-file-ignores=\"tests/conftest.py:E402\"\n    - name: Test with pytest\n      run: |\n        pytest ./tests -v\n"
  },
  {
    "path": ".github/workflows/python-publish.yml",
    "content": "# This workflow will upload a Python Package using Twine when a release is created\n# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries\n\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\nname: Upload Python Package\n\non:\n  release:\n    types: [published]\n\njobs:\n  deploy:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: Set up Python\n      uses: actions/setup-python@v2\n      with:\n        python-version: '3.x'\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip setuptools wheel\n        pip install build\n    - name: Build package\n      run: python -m build\n    - name: Publish package\n      uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29\n      with:\n        user: __token__\n        password: ${{ secrets.PYPI_PASSWORD }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n\n# Wing Pro\n*.wpr\n*.wpu"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2020 Sergey Totmyanin\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\">\n  <img src=\"https://i.imgur.com/8aQf6On.png\" />\n</p>\n\n# Unicaps\n[![PyPI pyversions](https://img.shields.io/pypi/pyversions/unicaps.png)](https://pypi.python.org/pypi/unicaps/)\n[![PyPI version](https://img.shields.io/pypi/v/unicaps)](https://pypi.python.org/pypi/unicaps/)\n[![PyPI status](https://img.shields.io/pypi/status/unicaps.png)](https://pypi.python.org/pypi/unicaps/)\n[![CodeFactor](https://www.codefactor.io/repository/github/sergey-scat/unicaps/badge)](https://www.codefactor.io/repository/github/sergey-scat/unicaps)\n\nUnicaps is a unified Python API for CAPTCHA solving services.\n\n\n⚠ **PLEASE NOTE**</br>\n⚠ A solving service API key is required to use this package!</br>\n⚠ The list of the supported services you can find in the table below.\n\n\n## Key Features\n - A unified Python interface that is independent of the service used\n - Uses native service protocol/endpoints (eg, no needs in patching _hosts_ file)\n - Has both synchronous and asynchronous client\n - Supports 10 types of CAPTCHAs\n - Supports 6 CAPTCHA solving services\n - Written Pythonic way and is intended for humans\n\n## Installation\n```pip install -U unicaps```\n\n## Simple Usage Example\n```python\n>>> from unicaps import CaptchaSolver, CaptchaSolvingService\n>>> solver = CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, api_key=\"<PLACE_YOUR_API_KEY_HERE>\")\n>>> solver.get_balance()\n2.84161\n>>> solved = solver.solve_image_captcha(open(\"captcha.jpg\", \"rb\"), is_phrase=False, is_case_sensitive=True)\n>>> solved.solution.text\n'w93Bx'\n>>> solved.cost\n0.00078\n>>> solved.report_good()\nTrue\n```\n\n## Asynchronous Example\n```python\nimport asyncio\nfrom pathlib import Path\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService\n\nAPI_KEY = '<PLACE_YOUR_API_KEY_HERE>'\n\nasync def main():\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        solved = await solver.solve_image_captcha(\n            Path(\"captcha.jpg\"),\n            is_phrase=False,\n            is_case_sensitive=True\n        )\n        print(f'CAPTCHA text: {solved.solution.text}')\n        await solved.report_good()\n\nif __name__ == '__main__':\n    asyncio.run(main())\n```\n\n## Supported CAPTCHAs / Services\n| 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/)\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ |\n| [azcaptcha.com](https://azcaptcha.com) | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ |\n| [deathbycaptcha.com](https://www.deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |\n\n### Image CAPTCHA\n| Service | Regular | Case Sensitive | Phrase | Numbers only | Letters only | Math | Length | Language | Comment for worker\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Cyrillic/Latin | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Latin | ✅ |\n| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Latin | ✅ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Latin | ✅ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | Latin | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | Cyrillic/Latin | ✅ |\n\n### Text CAPTCHA\n<details closed>\n<summary>What is this?</summary>\n\n<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.\n  For example: \"If tomorrow is Saturday, what day is today?\".</i>\n</details>\n\n| Service | Language |\n| ------------- | :---: |\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| English, Russian |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ❌ |\n| [azcaptcha.com](https://azcaptcha.com/) | ❌ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | English, Russian |\n\n### reCAPTCHA v2\n| Service | Regular | Invisible | Enterprise | Google service<sup>1</sup> | Proxy<sup>2</sup> | Cookies<sup>3</sup> | User-Agent<sup>4</sup> |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |\n| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |\n\n<sup>1</sup> Support of solving reCAPTCHA on Google services (e.g. Google Search) </br>\n<sup>2</sup> Support of solving via proxy server </br>\n<sup>3</sup> Support of passing custom cookies </br>\n<sup>4</sup> Support of passing custom User-Agent header </br>\n\n### reCAPTCHA v3\n| Service | Regular | Enterprise | Proxy | Cookies | User-Agent |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ❌ | ❌ | ❌ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ❌ | ❌ | ❌ |\n| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ❌ | ✅ | ❌ | ❌ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ✅ | ✅ | ✅ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ✅ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ❌ | ❌ | ❌ |\n\n### FunCaptcha (Arkose Labs)\n| Service | Regular | Data (BLOB) | Proxy | Cookies | User-Agent |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ✅ | ❌ | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ❌ | ✅ |\n| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ✅ | ✅ | ❌ | ✅ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ✅ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ❌ | ✅ |\n\n### KeyCAPTCHA\n| Service | Regular | Proxy | Cookies | User-Agent |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ❌ | ❌ | ❌ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ❌ | ❌ | ❌ | ❌ |\n| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ❌ | ❌ | ❌ |\n\n### Geetest\n| Service | Regular | API server | GetLib | Proxy | Cookies | User-Agent |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |\n| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [cap.guru](https://captcha.guru/ru/reg/?ref=127872) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |\n\n### Geetest v4\n| Service | Regular | Proxy | Cookies | User-Agent |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ❌ | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ❌ | ✅ |\n| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ❌ | ✅ |\n\n### hCaptcha\n| Service | Regular | Invisible | Custom Data | Proxy | Cookies | User-Agent |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ |\n| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ |\n\n### Capy Puzzle\n| Service | Regular | API server | Proxy | Cookies | User-Agent |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ✅ | ❌ | ❌ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [azcaptcha.com](https://azcaptcha.com/) | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ❌ | ❌ | ❌ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ❌ | ❌ |\n\n## Supported Proxy types\n| Service | HTTP | HTTPS | SOCKS 4 | SOCKS 5 |\n| ------------- | :---: | :---:\t| :---:\t| :---:\t|\n| [2captcha.com](http://2captcha.com/?from=8754088)\t| ✅ | ✅ | ✅ | ✅ |\n| [anti-captcha.com](http://getcaptchasolution.com/vus77mnl48) | ✅ | ✅ | ✅ | ✅ |\n| [azcaptcha.com](https://azcaptcha.com/) | ✅ | ✅ | ✅ | ✅ |\n| [cap.guru](https://cap.guru/ru/reg/?ref=127872) | ✅ | ✅ | ✅ | ✅ |\n| [deathbycaptcha.com](http://deathbycaptcha.com/?refid=1236988509) | ✅ | ❌ | ❌ | ❌ |\n| [rucaptcha.com](https://rucaptcha.com?from=9863637) | ✅ | ✅ | ✅ | ✅ |\n\n## How to...\n### Common\n<details>\n<summary>Get balance</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.ANTI_CAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    balance = solver.get_balance()\n```\n</details>\n\n<details>\n<summary>Get service status (is the service is up?)</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.ANTI_CAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # get status of the service (True - everything is Okay, False - probably the service is down)\n    status = solver.get_status()\n```\n</details>\n\n<details>\n<summary>Get technical details after solving</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# init captcha solver and solve the captcha\nwith CaptchaSolver(CaptchaSolvingService.ANTI_CAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    solved = solver.solve_...(...)\n\n    # get cost of the solving\n    cost = solved.cost\n\n    # get cookies (if any)\n    cookies = solved.cookies\n\n    # report good captcha\n    solved.report_good()\n\n    # report bad captcha\n    solved.report_bad()\n\n    # get solving start time\n    start_time = solved.start_time\n\n    # get solving end time\n    end_time = solved.end_time\n```\n</details>\n\n### CAPTCHAs\n<details>\n<summary>Solve Image CAPTCHA</summary>\n\n```python\nimport pathlib\n\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\nfrom unicaps.common import CaptchaCharType, CaptchaAlphabet\n\n# image file: it can be a Path, file-object or bytes.\nimage_file = pathlib.Path(r'/tmp/captcha.png')\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_image_captcha(\n        image=image_file,\n        char_type=CaptchaCharType.ALPHA,  # optional\n        is_phrase=False,  # optional\n        is_case_sensitive=True,  # optional\n        is_math=False,  # optional\n        min_len=4,  # optional\n        max_len=6,  # optional\n        alphabet=CaptchaAlphabet.LATIN,  # optional\n        comment='Type RED letters only'  # optional\n    )\n    # get CAPTCHA text\n    token = solved.solution.text\n```\n</details>\n\n<details>\n<summary>Solve reCAPTCHA v2</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get page URL and site_key from your page\npage_url = ...\nsite_key = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_recaptcha_v2(\n        site_key=site_key,\n        page_url=page_url,\n        data_s='<data-s value>',  # optional\n        api_domain='<\"google.com\" or \"recaptcha.net\">'  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve reCAPTCHA v2 Invisible</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get page url and site_key from your page\npage_url = ...\nsite_key = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_recaptcha_v2(\n        site_key=site_key,\n        page_url=page_url,\n        is_invisible=True,\n        data_s='<data-s value>',  # optional\n        api_domain='<\"google.com\" or \"recaptcha.net\">'  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve reCAPTCHA v2 Enterprise</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get page URL, site_key and data_s from your page\npage_url = ...\nsite_key = ...\ndata_s = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_recaptcha_v2(\n        site_key=site_key,\n        page_url=page_url,\n        is_enterprise=True,\n        data_s=data_s,  # optional\n        api_domain='<\"google.com\" or \"recaptcha.net\">'  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve reCAPTCHA v3</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npage_url = ...\nsite_key = ...\naction = ...\nmin_score = 0.7\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_recaptcha_v3(\n        site_key=site_key,\n        page_url=page_url,\n        action=action,  # optional\n        min_score=min_score,  # optional\n        api_domain='<\"google.com\" or \"recaptcha.net\">'  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve reCAPTCHA v3 Enterprise</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npage_url = ...\nsite_key = ...\naction = ...\nmin_score = 0.7\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_recaptcha_v3(\n        site_key=site_key,\n        page_url=page_url,\n        is_enterprise=True,\n        action=action,  # optional\n        min_score=min_score,  # optional\n        api_domain='<\"google.com\" or \"recaptcha.net\">'  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve hCaptcha</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npage_url = ...\nsite_key = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_hcaptcha(\n        site_key=site_key,\n        page_url=page_url,\n        api_domain=<\"hcaptcha.com\" or \"js.hcaptcha.com\">  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve hCaptcha Invisible</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npage_url = ...\nsite_key = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_hcaptcha(\n        site_key=site_key,\n        page_url=page_url,\n        is_invisible=True,\n        api_domain=<\"hcaptcha.com\" or \"js.hcaptcha.com\">  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve FunCaptcha</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npublic_key = ...\npage_url = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_funcaptcha(\n        public_key=public_key,\n        page_url=page_url,\n        service_url='<value of surl parameter>',  # optional\n        blob='<value of data[blob] parameter>'  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve KeyCaptcha</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npage_url = ...\nuser_id = ...\nsession_id = ...\nws_sign = ...\nws_sign2 = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_keycaptcha(\n        page_url=page_url,\n        user_id=user_id,\n        session_id=session_id,\n        ws_sign=ws_sign,\n        ws_sign2=ws_sign2\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve Geetest</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npage_url = ...\ngt_key = ...\nchallenge = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_geetest(\n        page_url=page_url,\n        gt_key=gt_key,\n        challenge=challenge,\n        api_server='<value of api_server parameter>'  # optional\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Solve Geetest v4</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\npage_url = ...\ncaptcha_id = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_geetest_v4(\n        page_url=page_url,\n        captcha_id=captcha_id\n    )\n\n    # get solution data\n    lot_number = solved.solution.lot_number\n    pass_token = solved.solution.pass_token\n    gen_time = solved.solution.gen_time\n    captcha_output = solved.solution.captcha_output\n```\n</details>\n\n<details>\n<summary>Solve Capy Puzzle</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\n\n# get CAPTCHA params from the target page/site\nsite_key = ...\npage_url = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_capy_puzzle(\n        site_key=site_key,\n        page_url=page_url,\n        api_server='<for example \"https://api.capy.me\">',  # optional\n        challenge_type='<\"puzzle\" or \"avatar\">'  # optional\n    )\n\n    # get solution data\n    captchakey = solved.solution.captchakey\n    challengekey = solved.solution.challengekey\n    answer = solved.solution.answer\n```\n</details>\n\n<details>\n<summary>Solve a text CAPTCHA</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\nfrom unicaps.common import CaptchaAlphabet, WorkerLanguage\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_text_captcha(\n        text='Si mañana es domingo, ¿qué día es hoy?',\n        alphabet=CaptchaAlphabet.LATIN,  # optional\n        language=WorkerLanguage.SPANISH  # optional\n    )\n\n    # get answer\n    answer = solved.solution.text  # Sábado\n```\n</details>\n\n### Error handling\n<details>\n<summary>Catch exceptions</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    try:\n        solved = solver.solve_recaptcha_v2(\n            site_key=site_key,\n            page_url=page_url\n        )\n    except exceptions.AccessDeniedError:  # wrong API key or the current IP is banned\n        pass\n    except exceptions.LowBalanceError:  # low balance\n        pass\n    except exceptions.ServiceTooBusy:  # no available slots to solve CAPTCHA\n        pass\n    except exceptions.SolutionWaitTimeout:  # haven't received a solution within N minutes\n        pass\n    except exceptions.TooManyRequestsError:  # request limit exceeded\n        pass\n    except exceptions.BadInputDataError:  # bad CAPTCHA data (bad image, wrong URL, etc.)\n        pass\n    except exceptions.UnableToSolveError:  # CAPTCHA unsolvable\n        pass\n    except exceptions.ProxyError:  # bad proxy\n        pass\n    except exceptions.NetworkError:  # network connection error\n        pass\n    else:\n        # get response token\n        token = solved.solution.token\n```\n</details>\n\n### Misc\n<details>\n<summary>Create a task and wait for the result</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\nfrom unicaps.captcha import RecaptchaV2\n\n# get page URL and site_key from your page\npage_url = ...\nsite_key = ...\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # create a task\n    task = solver.create_task(\n        RecaptchaV2(site_key, page_url)\n    )\n\n    # print task ID\n    print(task.task_id)\n\n    # wait for task to be completed\n    solved = task.wait()\n\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n<details>\n<summary>Add proxy, cookies and User-Agent</summary>\n\n```python\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\nfrom unicaps.proxy import ProxyServer\n\n# get page URL and site_key from your page\npage_url = ...\nsite_key = ...\nproxy = 'http://user:password@domain.com:8080'\nuser_agent = '<USER AGENT STRING>'\ncookies = {'name': 'value', ...}\n\n# init captcha solver\nwith CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, \"<PLACE YOUR API KEY HERE>\") as solver:\n    # solve CAPTCHA\n    solved = solver.solve_recaptcha_v2(\n        site_key=site_key,\n        page_url=page_url,\n        proxy=ProxyServer(proxy),\n        user_agent=user_agent,\n        cookies=cookies\n    )\n    # get response token\n    token = solved.solution.token\n```\n</details>\n\n## Real-life code examples\n[Examples](https://github.com/sergey-scat/unicaps/tree/master/examples)\n\n## Buy me a coffee\nhttps://www.buymeacoffee.com/sergey.scat\n"
  },
  {
    "path": "acceptance_tests.py",
    "content": "\"\"\"\nAcceptance tests\n\"\"\"\n\nimport asyncio\nimport logging\nimport os\nfrom importlib import import_module\n\nfrom unicaps import CaptchaSolver, AsyncCaptchaSolver\nfrom unicaps.__version__ import __version__\n\nlogging.basicConfig(level=logging.INFO)\n\nSERVICES = {\n    '2captcha.com': 'API_KEY_2CAPTCHA',\n    'anti-captcha.com': 'API_KEY_ANTICAPTCHA',\n    'azcaptcha.com': 'API_KEY_AZCAPTCHA',\n    'captcha.guru': 'API_KEY_CAPTCHA_GURU',\n    'cptch.net': 'API_KEY_CPTCH_NET',\n    'deathbycaptcha.com': 'API_KEY_DEATHBYCAPTCHA'\n}\n\nEXAMPLES = [\n    'image',\n    'recaptcha_v2',\n    'recaptcha_v2_invisible',\n    'recaptcha_v2_enterprise',\n    'recaptcha_v3',\n    'hcaptcha',\n    'keycaptcha',\n    'geetest',\n    'geetest_v4',\n    'capy_puzzle',\n    'text',\n    'funcaptcha'\n]\n\n\ndef main():\n    for service_name, env_var_name in SERVICES.items():\n        api_key = os.getenv(env_var_name)\n        if not api_key:\n            logging.error(\n                'Unable to read API key for the \"%s\" service. '\n                'The environment variable \"%s\" doesn\\'t exist!',\n                service_name,\n                env_var_name\n            )\n            continue\n\n        logging.info('######### Service: %s #########', service_name)\n\n        with CaptchaSolver(service_name, api_key) as solver:\n            status = solver.get_status()\n            balance = solver.get_balance()\n            logging.info('Status: %s.  Balance: %.2f', 'OK' if status else 'ERROR', balance)\n\n            for example_name in EXAMPLES:\n                logging.info('Current module: %s', example_name)\n                module = import_module(f'examples.{example_name}')\n                try:\n                    module.run(solver)\n                except Exception:\n                    logging.exception('%s run exception', module)\n\n\nasync def async_main():\n    for service_name, env_var_name in SERVICES.items():\n        api_key = os.getenv(env_var_name)\n        if not api_key:\n            logging.error(\n                'Unable to read API key for the \"%s\" service. '\n                'The environment variable \"%s\" doesn\\'t exist!',\n                service_name,\n                env_var_name\n            )\n            continue\n\n        logging.info('######### Service: %s #########', service_name)\n        async with AsyncCaptchaSolver(service_name, api_key) as async_solver:\n            status = await async_solver.get_status()\n            balance = await async_solver.get_balance()\n            logging.info(\n                'Status: %s.  Balance: %.2f',\n                'OK' if status else 'ERROR',\n                balance\n            )\n\n            for example_name in EXAMPLES:\n                logging.info('Current module: %s', example_name)\n                module = import_module(f'examples.async_{example_name}')\n                try:\n                    await module.run(async_solver)\n                except Exception:\n                    logging.exception('%s run exception', module)\n\n\nif __name__ == '__main__':\n    main()\n    asyncio.run(async_main(), debug=False)\n"
  },
  {
    "path": "examples/README.MD",
    "content": "\n# Real-life examples\nHere are some real-life examples of solving captchas, both with a synchronous client and asynchronous.\nBy default, all examples use 2captcha.com.\n\n## Prerequisites\n- The `httpx` (with HTTP/2 support) and `lxml` packages must be installed before running the examples:\n\n```pip install httpx[http2] lxml```\n\n- You must also specify your API key:\n  - either through an environment variable (`API_KEY_2CAPTCHA`);\n  - or directly in the code (replace the `<PLACE_YOUR_API_KEY_HERE>` line with the key value).\n\n- 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.\n\n## Proxy usage example\nSee examples on reCAPTCHA v2 Enterprise.\n"
  },
  {
    "path": "examples/async_capy_puzzle.py",
    "content": "\"\"\"\nCapy Puzzle CAPTCHA solving example\n\"\"\"\n\nimport asyncio\nimport os\nfrom urllib.parse import urlparse, parse_qs\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://www.capy.me/products/puzzle_captcha/'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\nUSER_AGENT = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '\n              '(KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Solve Capy Puzzle CAPTCHA \"\"\"\n\n    # create an HTTP2 session\n    async with httpx.AsyncClient(http2=True,\n                                 headers={'User-Agent': USER_AGENT}) as session:\n        # open page and extract CAPTCHA URL\n        response = await session.get(URL)\n        page = html.document_fromstring(response.text)\n\n        capy_url = page.xpath('//script[contains(@src, \"/puzzle/get_js/\")]')[0].attrib['src']\n        capy_url_parsed = urlparse(capy_url)\n        api_server = f'{capy_url_parsed.scheme}://{capy_url_parsed.hostname}'\n        site_key = parse_qs(capy_url_parsed.query)['k'][0]\n\n        # solve Capy Puzzle CAPTCHA\n        try:\n            solved = await solver.solve_capy_puzzle(\n                site_key=site_key,\n                page_url=URL,\n                api_server=api_server,\n                user_agent=USER_AGENT\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'Capy Puzzle CAPTCHA solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = await session.post(\n            URL,\n            data={\n                'capy_captchakey': solved.solution.captchakey,\n                'capy_challengekey': solved.solution.challengekey,\n                'capy_answer': solved.solution.answer\n            }\n        )\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//div[@class=\"result success\"]'):\n        print('The Capy Puzzle CAPTCHA has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The Capy Puzzle CAPTCHA has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_funcaptcha.py",
    "content": "\"\"\"\nFunCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://client-demo.arkoselabs.com/solo-animals'\nURL_VERIFY = 'https://client-demo.arkoselabs.com/solo-animals/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Load and solve FunCaptcha \"\"\"\n\n    # create an HTTP2 session\n    async with httpx.AsyncClient(http2=True) as session:\n        # open page and extract CAPTCHA URL\n        response = await session.get(URL)\n        page = html.document_fromstring(response.text)\n\n        # extract Public Key and Service URL values\n        script = page.xpath('//script[contains(text(), \"public_key\")]')[0].text\n        regexp = re.search(\n            r'public_key: ?\"([0-9A-Z-]+)\",\\s*surl: ?\"(.*)\",',\n            script\n        )\n        public_key = regexp.group(1)\n        service_url = regexp.group(2)\n\n        # solve FunCaptcha\n        try:\n            solved = await solver.solve_funcaptcha(\n                public_key=public_key,\n                page_url=URL,\n                service_url=service_url\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'FunCaptcha solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = await session.post(\n            URL_VERIFY,\n            data={\n                'name': 'test',\n                'verification-token': solved.solution.token,\n                'fc-token': solved.solution.token\n            }\n        )\n\n    # check the result\n    if '<h3>Solved!</h3>' in response.text:\n        print('The FunCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The FunCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_geetest.py",
    "content": "\"\"\"\nGeeTest solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport time\n\nimport httpx\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://2captcha.com/demo/geetest'\nINIT_PARAMS_URL = 'https://2captcha.com/api/v1/captcha-demo/gee-test/init-params?t={ms}'\nURL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Solve GeeTest \"\"\"\n\n    # create an HTTP2 session\n    async with httpx.AsyncClient(http2=True) as session:\n        # open page and extract CAPTCHA URL\n        response = await session.get(INIT_PARAMS_URL.format(ms=int(time.time() * 1000)))\n        init_params = response.json()\n\n        # solve GeeTest\n        try:\n            solved = await solver.solve_geetest(\n                page_url=URL,\n                gt_key=init_params['gt'],\n                challenge=init_params['challenge']\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'GeeTest solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = await session.post(\n            URL_VERIFY,\n            json={\n                'geetest_challenge': solved.solution.challenge,\n                'geetest_seccode': solved.solution.seccode,\n                'geetest_validate': solved.solution.validate\n            }\n        )\n\n    # check the result\n    if response.json().get('success'):\n        print('The GeeTest has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The GeeTest has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_geetest_v4.py",
    "content": "\"\"\"\nGeeTest v4 solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://2captcha.com/demo/geetest-v4'\nURL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test-v4/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Solve GeeTest v4 \"\"\"\n\n    # create an HTTP2 session\n    async with httpx.AsyncClient(http2=True) as session:\n        # open page and extract CAPTCHA URL\n        response = await session.get(URL)\n        page = html.document_fromstring(response.text)\n        captcha_js_url = page.xpath(\n            '//script[@data-chunk=\"pages-CaptchaDemo\" and '\n            'starts-with(@src, \"/dist/web/pages-CaptchaDemo.\")]'\n        )[0].attrib['src']\n        captcha_js_url = urljoin(URL, captcha_js_url)\n\n        # load pages-CaptchaDemo js-file and extract captcha ID\n        response = await session.get(captcha_js_url)\n        regexp = re.search(\n            r'window\\.initGeetest4\\(\\{\\s*captchaId:\\s?\"([0-9a-z]+)\"',\n            response.text\n        )\n\n        # solve GeeTest v4\n        try:\n            solved = await solver.solve_geetest_v4(\n                page_url=URL,\n                captcha_id=regexp.group(1)\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'GeeTestV4 solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = await session.post(\n            URL_VERIFY,\n            json={\n                'captcha_id': solved.solution.captcha_id,\n                'lot_number': solved.solution.lot_number,\n                'pass_token': solved.solution.pass_token,\n                'gen_time': solved.solution.gen_time,\n                'captcha_output': solved.solution.captcha_output\n            }\n        )\n\n    # check the result\n    if response.json().get('result') == 'success':\n        print('The GeeTest v4 has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The GeeTest v4 has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_hcaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nhCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://accounts.hcaptcha.com/demo'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Solve hCaptcha \"\"\"\n\n    # make a session and go to URL\n    async with httpx.AsyncClient(http2=True) as session:\n        response = await session.get(URL)\n\n        # parse page and get site-key\n        page = html.document_fromstring(response.text)\n        site_key = page.cssselect('.h-captcha')[0].attrib['data-sitekey']\n\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        form_data = {}\n        for input_data in page_form_data:\n            if input_data.xpath('@name'):\n                form_data[input_data.xpath('@name')[0]] = next(\n                    iter(input_data.xpath('@value')),\n                    None\n                )\n\n        # solve hCaptcha\n        try:\n            solved = await solver.solve_hcaptcha(\n                site_key=site_key,\n                page_url=URL\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'hCaptcha solving exception: {str(exc)}')\n            return False, None\n\n        # add token to form data\n        form_data['h-captcha-response'] = solved.solution.token\n        form_data['g-recaptcha-response'] = solved.solution.token\n\n        # post form data\n        response = await session.post(URL, data=form_data)\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//div[text()=\"Verification Success!\"]'):\n        print('The hCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The hCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_image.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nImage CAPTCHA solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\nfrom unicaps.common import CaptchaCharType, CaptchaAlphabet  # type: ignore\n\nURL = 'https://democaptcha.com/demo-form-eng/image.html'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Solve image CAPTCHA \"\"\"\n\n    # make a session and go to URL\n    async with httpx.AsyncClient(http2=True) as session:\n        response = await session.get(URL)\n\n        # parse page and get captcha URL\n        page = html.document_fromstring(response.text)\n\n        # fetch captcha image\n        captcha_url = page.cssselect('#htest_image')[0].attrib['src']\n        captcha_response = await session.get(captcha_url)\n\n        # solve the captcha\n        try:\n            solved = await solver.solve_image_captcha(\n                image=captcha_response.content,  # binary image data\n                char_type=CaptchaCharType.ALPHANUMERIC,  # consists of alphanumeric characters\n                is_phrase=False,  # not a phrase (no whitespaces)\n                is_case_sensitive=True,  # case-sensitive text\n                is_math=False,  # no calculation needed\n                alphabet=CaptchaAlphabet.LATIN  # latin alphabet being used\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'Image CAPTCHA solving exception: {str(exc)}')\n            return False, None\n\n        form_data = {}\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        for input_data in page_form_data:\n            if input_data.xpath('@name'):\n                form_data[str(input_data.xpath('@name')[0])] = str(\n                    next(iter(input_data.xpath('@value')), None)\n                )\n\n        form_data['message'] = 'test'\n        # add token to form data\n        form_data['vericode'] = solved.solution.text\n\n        # post form data and parse result page\n        response = await session.post(URL, data=form_data)\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//h2[text()=\"Your message has been sent (actually not), thank you!\"]'):\n        print('The Image CAPTCHA has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The Image CAPTCHA has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_keycaptcha.py",
    "content": "\"\"\"\nKeyCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://2captcha.com/demo/keycaptcha'\nURL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/key-captcha/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Get and solve KeyCaptcha \"\"\"\n\n    # create an HTTP2 session\n    async with httpx.AsyncClient(http2=True) as session:\n        # open page and extract CAPTCHA URL\n        response = await session.get(URL)\n        page = html.document_fromstring(response.text)\n\n        # extract captcha URL, parse page and get captcha params\n        captcha_url = page.xpath('//iframe[@name=\"key-captcha-widget\"]')[0].attrib['src']\n        captcha_url = urljoin(URL, captcha_url)\n        response = await session.get(captcha_url)\n        captcha_page = html.document_fromstring(response.text)\n        script = captcha_page.xpath('//script[contains(text(), \"var s_s_c_user_id\")]')[0].text\n\n        def extract_var_value(var_name):\n            return re.search(fr\"var {var_name} = '(.+)';\", script).group(1)\n\n        s_s_c_user_id = extract_var_value('s_s_c_user_id')\n        s_s_c_session_id = extract_var_value('s_s_c_session_id')\n        s_s_c_web_server_sign = extract_var_value('s_s_c_web_server_sign')\n        s_s_c_web_server_sign2 = extract_var_value('s_s_c_web_server_sign2')\n\n        # solve KeyCaptcha\n        try:\n            solved = await solver.solve_keycaptcha(\n                page_url=URL,\n                user_id=s_s_c_user_id,\n                session_id=s_s_c_session_id,\n                ws_sign=s_s_c_web_server_sign,\n                ws_sign2=s_s_c_web_server_sign2\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'KeyCaptcha solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = await session.post(\n            URL_VERIFY,\n            json={'capcode': solved.solution.token}\n        )\n\n    # check the result\n    if response.json().get('success'):\n        print('The KeyCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The KeyCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_recaptcha_v2.py",
    "content": "\"\"\"\nreCAPTCHA v2 async solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Get and solve reCAPTCHA v2 \"\"\"\n\n    # make a session and go to URL\n    async with httpx.AsyncClient(http2=True) as session:\n        response = await session.get(URL)\n\n        # parse page and get site-key\n        page = html.document_fromstring(response.text)\n        site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']\n\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        form_data = {}\n        for input_data in page_form_data:\n            form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]\n\n        # solve reCAPTCHA\n        try:\n            solved = await solver.solve_recaptcha_v2(\n                site_key=site_key,\n                page_url=URL\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v2 solving exception: {str(exc)}')\n            return False, None\n\n        # add token to form data\n        form_data['g-recaptcha-response'] = solved.solution.token\n\n        # post form data\n        response = await session.post(URL, data=form_data)\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//h2[text()=\"Success!\"]'):\n        print('The reCAPTCHA v2 has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The reCAPTCHA v2 has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_recaptcha_v2_enterprise.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 Enterprise solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport random\nimport string\n\nimport httpx\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\nfrom unicaps.proxy import ProxyServer  # type: ignore\n\nURL = 'https://store.steampowered.com/join'\nURL_REFRESH_CAPTCHA = 'https://store.steampowered.com/join/refreshcaptcha/'\nURL_VERIFY_EMAIL = 'https://store.steampowered.com/join/ajaxverifyemail'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\nUSER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'\nPROXY = os.getenv(\n    'HTTP_PROXY_SERVER',\n    default='http://<LOGIN>:<PASSWORD>@<PROXY_ADDRESS>:<PORT>'\n)\n\n\ndef get_random_word(length):\n    \"\"\" Generate a random word of a given length \"\"\"\n    letters = string.ascii_lowercase + string.digits\n    return ''.join(random.choice(letters) for i in range(length))\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Get and solve CAPTCHA \"\"\"\n\n    # make a session, update headers and proxies\n    async with httpx.AsyncClient(proxies=PROXY) as session:\n        session.headers.update({\n            'User-Agent': USER_AGENT,\n        })\n\n        # open the \"Join\" page just to get session cookies\n        response = await session.get(URL)\n\n        # get reCAPTCHA params\n        response = await session.post(\n            URL_REFRESH_CAPTCHA,\n            data=dict(count=1)\n        )\n        captcha_params = response.json()\n\n        # solve reCAPTCHA\n        try:\n            solved = await solver.solve_recaptcha_v2(\n                site_key=captcha_params['sitekey'],\n                page_url=URL,\n                data_s=captcha_params['s'],\n                is_enterprise=True,\n                proxy=ProxyServer(PROXY),\n                user_agent=USER_AGENT,\n                cookies=dict(session.cookies)\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v2 Enterprise solving exception: {str(exc)}')\n            return False, None\n\n        # generate email address\n        email = f'random_{get_random_word(10)}@gmail.com'\n\n        # verify email\n        response = await session.post(\n            URL_VERIFY_EMAIL,\n            data=dict(\n                email=email,\n                captchagid=captcha_params['gid'],\n                captcha_text=solved.solution.token,\n                elang=0\n            )\n        )\n        response_data = response.json()\n\n    print(f\"Email: {email}\\nResult: {response_data['details']}\")\n\n    # check the result\n    if 'the CAPTCHA appears to be invalid' not in response_data['details']:\n        print('The reCAPTCHA v2 Enterprise has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The reCAPTCHA v2 Enterprise has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_recaptcha_v2_invisible.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 invisible solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-invisible.php'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Get and solve reCAPTCHA v2 invisible \"\"\"\n\n    # make a session and go to URL\n    async with httpx.AsyncClient(http2=True) as session:\n        response = await session.get(URL)\n\n        # parse page and get site-key\n        page = html.document_fromstring(response.text)\n        site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']\n\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        form_data = {}\n        for input_data in page_form_data:\n            form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]\n\n        # solve reCAPTCHA\n        try:\n            solved = await solver.solve_recaptcha_v2(\n                site_key=site_key,\n                page_url=URL,\n                is_invisible=True\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v2 (invisible) solving exception: {str(exc)}')\n            return False, None\n\n        # add token to form data\n        form_data['g-recaptcha-response'] = solved.solution.token\n\n        # post form data\n        response = await session.post(URL, data=form_data)\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//h2[text()=\"Success!\"]'):\n        print('The Invisible reCAPTCHA v2 has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The Invisible reCAPTCHA v2 has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_recaptcha_v3.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v3 solving example\n\"\"\"\n\nimport asyncio\nimport os\nimport re\nfrom pprint import pprint\n\nimport httpx\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php'\nVERIFY_URL = ('https://recaptcha-demo.appspot.com/recaptcha-v3-verify.php'\n              '?action={action}&token={token}')\nMIN_SCORE = 0.7\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" Get and solve CAPTCHA \"\"\"\n\n    # make a session and go to URL\n    async with httpx.AsyncClient(http2=True) as session:\n        response = await session.get(URL)\n\n        # extract site-key and action from the page source using regular expression\n        regexp = re.search(\n            r\"grecaptcha.execute\\('([a-zA-Z0-9\\-_]{40})', ?\\{action: ?'(.*)'\\}\\)'\",\n            response.text\n        )\n        site_key = regexp.group(1)\n        action = regexp.group(2)\n\n        # solve reCAPTCHA\n        try:\n            solved = await solver.solve_recaptcha_v3(\n                site_key=site_key,\n                page_url=URL,\n                action=action,\n                min_score=MIN_SCORE\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v3 solving exception: {str(exc)}')\n            return False, None\n\n        # verify token and print the result\n        response = await session.get(\n            VERIFY_URL.format(\n                action=action,\n                token=solved.solution.token\n            )\n        )\n        result = response.json()\n        pprint(result)\n\n    # check the result\n    if not result['score']:\n        print('The reCAPTCHA v3 has not been solved correctly! Error codes: ' + ', '.join(\n            result['error-codes']))\n        # report bad CAPTCHA\n        await solved.report_bad()\n        return False, solved\n\n    if result['score'] < MIN_SCORE:\n        print(\n            f'Solved reCAPTCHA v3 score ({result[\"score\"]}) is less than requested ({MIN_SCORE})!'\n        )\n        # report bad CAPTCHA\n        await solved.report_bad()\n        return False, solved\n\n    print('The reCAPTCHA v3 has been solved correctly!')\n    # report good CAPTCHA\n    await solved.report_good()\n    return True, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/async_text.py",
    "content": "\"\"\"\nTextCaptcha solving example\n\"\"\"\n\nimport asyncio\nimport os\n\nfrom unicaps import AsyncCaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\nfrom unicaps.common import WorkerLanguage  # type: ignore\n\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\nTEXT_CAPTCHA = 'If tomorrow is Sunday, what day is today?'\nTEXT_CAPTCHA_ANSWER = 'saturday'\n\n\nasync def main():\n    \"\"\" Init AsyncCaptchaSolver and run the example \"\"\"\n    async with AsyncCaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as solver:\n        await run(solver)\n\n\nasync def run(solver):\n    \"\"\" TextCaptcha solving func \"\"\"\n\n    # solve TextCaptcha\n    try:\n        solved = await solver.solve_text_captcha(\n            text=TEXT_CAPTCHA,\n            language=WorkerLanguage.ENGLISH\n        )\n    except exceptions.UnicapsException as exc:\n        print(f'TextCaptcha solving exception: {str(exc)}')\n        return False, None\n\n    # check the result\n    if solved.solution.text.lower() == TEXT_CAPTCHA_ANSWER:\n        print('The TextCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        await solved.report_good()\n        return True, solved\n\n    print('The TextCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    await solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    asyncio.run(main())\n"
  },
  {
    "path": "examples/capy_puzzle.py",
    "content": "\"\"\"\nCapy Puzzle CAPTCHA solving example\n\"\"\"\n\nimport os\nfrom urllib.parse import urlparse, parse_qs\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://www.capy.me/products/puzzle_captcha/'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\nUSER_AGENT = ('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '\n              '(KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36')\n\n\ndef run(solver):\n    \"\"\" Solve Capy Puzzle CAPTCHA \"\"\"\n\n    # create an HTTP2 session\n    with httpx.Client(http2=True, headers={'User-Agent': USER_AGENT}) as session:\n        # open page and extract CAPTCHA URL\n        response = session.get(URL)\n        page = html.document_fromstring(response.text)\n\n        capy_url = page.xpath('//script[contains(@src, \"/puzzle/get_js/\")]')[0].attrib['src']\n        capy_url_parsed = urlparse(capy_url)\n        api_server = f'{capy_url_parsed.scheme}://{capy_url_parsed.hostname}'\n        site_key = parse_qs(capy_url_parsed.query)['k'][0]\n\n        # solve Capy Puzzle CAPTCHA\n        try:\n            solved = solver.solve_capy_puzzle(\n                site_key=site_key,\n                page_url=URL,\n                api_server=api_server,\n                user_agent=USER_AGENT\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'Capy Puzzle CAPTCHA solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = session.post(\n            URL,\n            data={\n                'capy_captchakey': solved.solution.captchakey,\n                'capy_challengekey': solved.solution.challengekey,\n                'capy_answer': solved.solution.answer\n            }\n        )\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//div[@class=\"result success\"]'):\n        print('The Capy Puzzle CAPTCHA has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The Capy Puzzle CAPTCHA has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/funcaptcha.py",
    "content": "\"\"\"\nFunCaptcha solving example\n\"\"\"\nimport os\nimport re\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://client-demo.arkoselabs.com/solo-animals'\nURL_VERIFY = 'https://client-demo.arkoselabs.com/solo-animals/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Load and solve FunCaptcha \"\"\"\n\n    # create an HTTP2 session\n    with httpx.Client(http2=True) as session:\n        # open page and extract CAPTCHA URL\n        response = session.get(URL)\n        page = html.document_fromstring(response.text)\n\n        # extract Public Key and Service URL values\n        script = page.xpath('//script[contains(text(), \"public_key\")]')[0].text\n        regexp = re.search(\n            r'public_key: ?\"([0-9A-Z-]+)\",\\s*surl: ?\"(.*)\",',\n            script\n        )\n        public_key = regexp.group(1)\n        service_url = regexp.group(2)\n\n        # solve FunCaptcha\n        try:\n            solved = solver.solve_funcaptcha(\n                public_key=public_key,\n                page_url=URL,\n                service_url=service_url\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'FunCaptcha solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = session.post(\n            URL_VERIFY,\n            data={\n                'name': 'test',\n                'verification-token': solved.solution.token,\n                'fc-token': solved.solution.token\n            }\n        )\n\n    # check the result\n    if '<h3>Solved!</h3>' in response.text:\n        print('The FunCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The FunCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/geetest.py",
    "content": "\"\"\"\nGeeTest solving example\n\"\"\"\n\nimport os\nimport time\n\nimport httpx\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://2captcha.com/demo/geetest'\nINIT_PARAMS_URL = 'https://2captcha.com/api/v1/captcha-demo/gee-test/init-params?t={ms}'\nURL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Solve GeeTest \"\"\"\n\n    # create an HTTP2 session\n    with httpx.Client(http2=True) as session:\n        # open page and extract CAPTCHA URL\n        response = session.get(INIT_PARAMS_URL.format(ms=int(time.time() * 1000)))\n        init_params = response.json()\n\n        # solve GeeTest\n        try:\n            solved = solver.solve_geetest(\n                page_url=URL,\n                gt_key=init_params['gt'],\n                challenge=init_params['challenge']\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'GeeTest solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = session.post(\n            URL_VERIFY,\n            json={\n                'geetest_challenge': solved.solution.challenge,\n                'geetest_seccode': solved.solution.seccode,\n                'geetest_validate': solved.solution.validate\n            }\n        )\n\n    # check the result\n    if response.json().get('success'):\n        print('The GeeTest has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The GeeTest has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/geetest_v4.py",
    "content": "\"\"\"\nGeeTest solving example\n\"\"\"\n\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://2captcha.com/demo/geetest-v4'\nURL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/gee-test-v4/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Solve GeeTest v4 \"\"\"\n\n    # create an HTTP2 session\n    with httpx.Client(http2=True) as session:\n        # open page and extract pages-CaptchaDemo URL\n        response = session.get(URL)\n        page = html.document_fromstring(response.text)\n        captcha_js_url = page.xpath(\n            '//script[@data-chunk=\"pages-CaptchaDemo\" and '\n            'starts-with(@src, \"/dist/web/pages-CaptchaDemo.\")]'\n        )[0].attrib['src']\n        captcha_js_url = urljoin(URL, captcha_js_url)\n\n        # load pages-CaptchaDemo js-file and extract captcha ID\n        response = session.get(captcha_js_url)\n        regexp = re.search(\n            r'window\\.initGeetest4\\(\\{\\s*captchaId:\\s?\"([0-9a-z]+)\"',\n            response.text\n        )\n\n        # solve GeeTest v4\n        try:\n            solved = solver.solve_geetest_v4(\n                page_url=URL,\n                captcha_id=regexp.group(1)\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'GeeTestV4 solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = session.post(\n            URL_VERIFY,\n            json={\n                'captcha_id': solved.solution.captcha_id,\n                'lot_number': solved.solution.lot_number,\n                'pass_token': solved.solution.pass_token,\n                'gen_time': solved.solution.gen_time,\n                'captcha_output': solved.solution.captcha_output\n            }\n        )\n\n    # check the result\n    if response.json().get('result') == 'success':\n        print('The GeeTest v4 has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The GeeTest v4 has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/hcaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nhCaptcha solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://accounts.hcaptcha.com/demo'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Solve hCaptcha \"\"\"\n\n    # make a session and go to URL\n    with httpx.Client(http2=True) as session:\n        response = session.get(URL)\n\n        # parse page and get site-key\n        page = html.document_fromstring(response.text)\n        site_key = page.cssselect('.h-captcha')[0].attrib['data-sitekey']\n\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        form_data = {}\n        for input_data in page_form_data:\n            if input_data.xpath('@name'):\n                form_data[input_data.xpath('@name')[0]] = next(\n                    iter(input_data.xpath('@value')),\n                    None\n                )\n\n        # solve hCaptcha\n        try:\n            solved = solver.solve_hcaptcha(\n                site_key=site_key,\n                page_url=URL\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'hCaptcha solving exception: {str(exc)}')\n            return False, None\n\n        # add token to form data\n        form_data['h-captcha-response'] = solved.solution.token\n        form_data['g-recaptcha-response'] = solved.solution.token\n\n        # post form data\n        response = session.post(URL, data=form_data)\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//div[text()=\"Verification Success!\"]'):\n        print('The hCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The hCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/image.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nImage CAPTCHA solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\nfrom unicaps.common import CaptchaCharType, CaptchaAlphabet  # type: ignore\n\nURL = 'https://democaptcha.com/demo-form-eng/image.html'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Solve image CAPTCHA \"\"\"\n\n    # make a session and go to URL\n    with httpx.Client(http2=True) as session:\n        response = session.get(URL)\n        response.raise_for_status()\n\n        # parse page and get captcha URL\n        page = html.document_fromstring(response.text)\n\n        # fetch captcha image\n        captcha_url = page.cssselect('#htest_image')[0].attrib['src']\n        captcha_response = session.get(captcha_url)\n        captcha_response.raise_for_status()\n\n        # solve the captcha\n        try:\n            solved = solver.solve_image_captcha(\n                image=captcha_response.content,  # binary image data\n                char_type=CaptchaCharType.ALPHANUMERIC,  # consists of alphanumeric characters\n                is_phrase=False,  # not a phrase (no whitespaces)\n                is_case_sensitive=True,  # case-sensitive text\n                is_math=False,  # no calculation needed\n                alphabet=CaptchaAlphabet.LATIN  # latin alphabet being used\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'Image CAPTCHA solving exception: {str(exc)}')\n            return False, None\n\n        form_data = {}\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        for input_data in page_form_data:\n            if input_data.xpath('@name'):\n                form_data[str(input_data.xpath('@name')[0])] = str(\n                    next(iter(input_data.xpath('@value')), None)\n                )\n\n        form_data['message'] = 'test'\n        # add token to form data\n        form_data['vericode'] = solved.solution.text\n\n        # post form data and parse result page\n        response = session.post(URL, data=form_data)\n        response.raise_for_status()\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//h2[text()=\"Your message has been sent (actually not), thank you!\"]'):\n        print('The Image CAPTCHA has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The Image CAPTCHA has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/keycaptcha.py",
    "content": "\"\"\"\nKeyCaptcha solving example\n\"\"\"\n\nimport os\nimport re\nfrom urllib.parse import urljoin\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://2captcha.com/demo/keycaptcha'\nURL_VERIFY = 'https://2captcha.com/api/v1/captcha-demo/key-captcha/verify'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Get and solve KeyCaptcha \"\"\"\n\n    # create an HTTP2 session\n    with httpx.Client(http2=True) as session:\n        # open page and extract CAPTCHA URL\n        response = session.get(URL)\n        page = html.document_fromstring(response.text)\n\n        # extract captcha URL, parse page and get captcha params\n        captcha_url = page.xpath('//iframe[@name=\"key-captcha-widget\"]')[0].attrib['src']\n        captcha_url = urljoin(URL, captcha_url)\n        response = session.get(captcha_url)\n        captcha_page = html.document_fromstring(response.text)\n        script = captcha_page.xpath('//script[contains(text(), \"var s_s_c_user_id\")]')[0].text\n\n        def extract_var_value(var_name):\n            return re.search(fr\"var {var_name} = '(.+)';\", script).group(1)\n\n        s_s_c_user_id = extract_var_value('s_s_c_user_id')\n        s_s_c_session_id = extract_var_value('s_s_c_session_id')\n        s_s_c_web_server_sign = extract_var_value('s_s_c_web_server_sign')\n        s_s_c_web_server_sign2 = extract_var_value('s_s_c_web_server_sign2')\n\n        # solve KeyCaptcha\n        try:\n            solved = solver.solve_keycaptcha(\n                page_url=URL,\n                user_id=s_s_c_user_id,\n                session_id=s_s_c_session_id,\n                ws_sign=s_s_c_web_server_sign,\n                ws_sign2=s_s_c_web_server_sign2\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'KeyCaptcha solving exception: {str(exc)}')\n            return False, None\n\n        # post solved captcha token\n        response = session.post(\n            URL_VERIFY,\n            json={'capcode': solved.solution.token}\n        )\n\n    # check the result\n    if response.json().get('success'):\n        print('The KeyCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The KeyCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/recaptcha_v2.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-checkbox.php'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Get and solve reCAPTCHA v2 \"\"\"\n\n    # make a session and go to URL\n    with httpx.Client(http2=True) as session:\n        response = session.get(URL)\n\n        # parse page and get data-sitekey value\n        page = html.document_fromstring(response.text)\n        site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']\n\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        form_data = {}\n        for input_data in page_form_data:\n            form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]\n\n        # solve reCAPTCHA\n        try:\n            solved = solver.solve_recaptcha_v2(\n                site_key=site_key,\n                page_url=URL\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v2 solving exception: {str(exc)}')\n            return False, None\n\n        # add token to form data\n        form_data['g-recaptcha-response'] = solved.solution.token\n\n        # post form data\n        response = session.post(URL, data=form_data)\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//h2[text()=\"Success!\"]'):\n        print('The reCAPTCHA v2 has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The reCAPTCHA v2 has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/recaptcha_v2_enterprise.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 Enterprise solving example\n\"\"\"\n\nimport os\nimport random\nimport string\nfrom urllib.parse import urlparse\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\nfrom unicaps.proxy import ProxyServer  # type: ignore\n\nURL = 'https://store.steampowered.com/join'\nURL_REFRESH_CAPTCHA = 'https://store.steampowered.com/join/refreshcaptcha/'\nURL_VERIFY_EMAIL = 'https://store.steampowered.com/join/ajaxverifyemail'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\nUSER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0'\nPROXY = os.getenv(\n    'HTTP_PROXY_SERVER',\n    default='http://<LOGIN>:<PASSWORD>@<PROXY_ADDRESS>:<PORT>'\n)\n\n\ndef get_random_word(length):\n    \"\"\" Generate a random word of a given length \"\"\"\n    letters = string.ascii_lowercase + string.digits\n    return ''.join(random.choice(letters) for i in range(length))\n\n\ndef run(solver):\n    \"\"\" Get and solve CAPTCHA \"\"\"\n\n    # make a session, update headers and proxies\n    with httpx.Client(proxies=PROXY) as session:\n        session.headers.update({\n            'User-Agent': USER_AGENT,\n        })\n\n        # open the \"Join\" page\n        response = session.get(URL)\n        response.raise_for_status()\n\n        # parse the page, then find and extract reCAPTCHA domain\n        page = html.document_fromstring(response.text)\n        recaptcha_url = page.xpath(\n            '//script[contains(@src, \"recaptcha/enterprise.js\")]')[0].attrib['src']\n        recaptcha_domain = urlparse(recaptcha_url).netloc\n\n        # get reCAPTCHA params\n        captcha_params = session.post(\n            URL_REFRESH_CAPTCHA,\n            data=dict(count=1)\n        ).json()\n\n        # solve reCAPTCHA\n        try:\n            solved = solver.solve_recaptcha_v2(\n                site_key=captcha_params['sitekey'],\n                page_url=URL,\n                is_enterprise=True,\n                data_s=captcha_params['s'],\n                api_domain=recaptcha_domain,\n                proxy=ProxyServer(PROXY),\n                user_agent=USER_AGENT\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v2 Enterprise solving exception: {str(exc)}')\n            return False, None\n\n        # generate email address\n        email = f'random_{get_random_word(10)}@gmail.com'\n\n        # verify email\n        response = session.post(\n            URL_VERIFY_EMAIL,\n            data=dict(\n                email=email,\n                captchagid=captcha_params['gid'],\n                captcha_text=solved.solution.token,\n                elang=0\n            )\n        )\n        response.raise_for_status()\n        response_data = response.json()\n\n    print(f\"Email: {email}\\nResult: {response_data['details']}\")\n\n    # check the result\n    if 'the CAPTCHA appears to be invalid' not in response_data['details']:\n        print('The reCAPTCHA v2 Enterprise has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The reCAPTCHA v2 Enterprise has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/recaptcha_v2_invisible.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v2 invisible solving example\n\"\"\"\n\nimport os\n\nimport httpx\nfrom lxml import html  # type: ignore\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://recaptcha-demo.appspot.com/recaptcha-v2-invisible.php'\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Get and solve reCAPTCHA v2 invisible \"\"\"\n\n    # make a session and go to URL\n    with httpx.Client(http2=True) as session:\n        response = session.get(URL)\n\n        # parse page and get site-key\n        page = html.document_fromstring(response.text)\n        site_key = page.cssselect('.g-recaptcha')[0].attrib['data-sitekey']\n\n        # parse form data\n        page_form_data = page.xpath('//form//input')\n        form_data = {}\n        for input_data in page_form_data:\n            form_data[input_data.xpath('@name')[0]] = input_data.xpath('@value')[0]\n\n        # solve reCAPTCHA\n        try:\n            solved = solver.solve_recaptcha_v2(\n                site_key=site_key,\n                page_url=URL,\n                is_invisible=True\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v2 (invisible) solving exception: {str(exc)}')\n            return False, None\n\n        # add token to form data\n        form_data['g-recaptcha-response'] = solved.solution.token\n\n        # post form data\n        response = session.post(URL, data=form_data)\n        page = html.document_fromstring(response.text)\n\n    # check the result\n    if page.xpath('//h2[text()=\"Success!\"]'):\n        print('The Invisible reCAPTCHA v2 has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The Invisible reCAPTCHA v2 has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/recaptcha_v3.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nreCAPTCHA v3 solving example\n\"\"\"\n\nimport os\nimport re\nfrom pprint import pprint\n\nimport httpx\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\n\nURL = 'https://recaptcha-demo.appspot.com/recaptcha-v3-request-scores.php'\nVERIFY_URL = ('https://recaptcha-demo.appspot.com/recaptcha-v3-verify.php'\n              '?action={action}&token={token}')\nMIN_SCORE = 0.7\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\n\n\ndef run(solver):\n    \"\"\" Get and solve CAPTCHA \"\"\"\n\n    # make a session and go to URL\n    with httpx.Client(http2=True) as session:\n        response = session.get(URL)\n\n        # extract site-key and action from the page source using regular expression\n        regexp = re.search(\n            r\"grecaptcha.execute\\('([a-zA-Z0-9\\-_]{40})', ?\\{action: ?'(.*)'\\}\\)'\",\n            response.text\n        )\n        site_key = regexp.group(1)\n        action = regexp.group(2)\n\n        # solve reCAPTCHA\n        try:\n            solved = solver.solve_recaptcha_v3(\n                site_key=site_key,\n                page_url=URL,\n                action=action,\n                min_score=MIN_SCORE\n            )\n        except exceptions.UnicapsException as exc:\n            print(f'reCAPTCHA v3 solving exception: {str(exc)}')\n            return False, None\n\n        # verify token and print the result\n        response = session.get(\n            VERIFY_URL.format(\n                action=action,\n                token=solved.solution.token\n            )\n        )\n        result = response.json()\n        pprint(result)\n\n    # check the result\n    if not result['score']:\n        print(\n            'The reCAPTCHA v3 has not been solved correctly! Error codes: ' + ', '.join(\n                result['error-codes'])\n        )\n        # report bad CAPTCHA\n        solved.report_bad()\n        return False, solved\n\n    if result['score'] < MIN_SCORE:\n        print(\n            f'Solved reCAPTCHA v3 score ({result[\"score\"]}) is less than requested ({MIN_SCORE})!'\n        )\n        # report bad CAPTCHA\n        solved.report_bad()\n        return False, solved\n\n    print('The reCAPTCHA v3 has been solved correctly!')\n    # report good CAPTCHA\n    solved.report_good()\n    return True, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "examples/requirements.txt",
    "content": "httpx[http2]\nlxml"
  },
  {
    "path": "examples/run_all.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nAll examples runner\n\"\"\"\n\nfrom importlib import import_module\nimport logging\nimport os\n\nfrom unicaps import CaptchaSolver  # type: ignore\n\n# services dict: key is a name of CAPTCHA solving service, value is an env variable containing\n# the API key\nSERVICES = {\n    '2captcha.com': 'API_KEY_2CAPTCHA',\n    'anti-captcha.com': 'API_KEY_ANTICAPTCHA',\n    'azcaptcha.com': 'API_KEY_AZCAPTCHA',\n    'captcha.guru': 'API_KEY_CAPTCHA_GURU',\n    'cptch.net': 'API_KEY_CPTCH_NET'\n}\n\n# list of modules containing CAPTCHA solving examples\nEXAMPLES = [\n    'image',\n    'recaptcha_v2',\n    'recaptcha_v2_invisible',\n    'recaptcha_v2_enterprise',\n    'recaptcha_v3',\n    'hcaptcha',\n    'keycaptcha',\n    'geetest',\n    'geetest_v4',\n    'capy_puzzle',\n    'text',\n    'funcaptcha'\n]\n\nlogging.basicConfig(level=logging.DEBUG)\n\n\nif __name__ == '__main__':\n    for service_name, env_var_name in SERVICES.items():\n        api_key = os.getenv(env_var_name)\n        print(f'######### Service: {service_name} #########')\n\n        # init captcha solver\n        with CaptchaSolver(service_name, api_key) as solver:\n            for example_name in EXAMPLES:\n                print(example_name)\n                module = import_module(example_name)\n                module.run(solver)\n                print()\n"
  },
  {
    "path": "examples/text.py",
    "content": "\"\"\"\nTextCaptcha solving example\n\"\"\"\n\nimport os\n\nfrom unicaps import CaptchaSolver, CaptchaSolvingService, exceptions  # type: ignore\nfrom unicaps.common import WorkerLanguage  # type: ignore\n\nAPI_KEY = os.getenv('API_KEY_2CAPTCHA', default='<PLACE_YOUR_API_KEY_HERE>')\nTEXT_CAPTCHA = 'If tomorrow is Sunday, what day is today?'\nTEXT_CAPTCHA_ANSWER = 'saturday'\n\n\ndef run(solver):\n    \"\"\" Solve TextCaptcha \"\"\"\n\n    # solve TextCaptcha\n    try:\n        solved = solver.solve_text_captcha(\n            text=TEXT_CAPTCHA,\n            language=WorkerLanguage.ENGLISH\n        )\n    except exceptions.UnicapsException as exc:\n        print(f'TextCaptcha solving exception: {str(exc)}')\n        return False, None\n\n    # check the result\n    if solved.solution.text.lower() == TEXT_CAPTCHA_ANSWER:\n        print('The TextCaptcha has been solved correctly!')\n        # report good CAPTCHA\n        solved.report_good()\n        return True, solved\n\n    print('The TextCaptcha has not been solved correctly!')\n    # report bad CAPTCHA\n    solved.report_bad()\n    return False, solved\n\n\nif __name__ == '__main__':\n    with CaptchaSolver(CaptchaSolvingService.TWOCAPTCHA, API_KEY) as captcha_solver:\n        run(captcha_solver)\n"
  },
  {
    "path": "requirements-dev.txt",
    "content": "flake8>=3.8.3\npytest>=6.0.1\ntox>=3.17.1"
  },
  {
    "path": "requirements.txt",
    "content": "httpx>=0.22.0\nenforce-typing\n"
  },
  {
    "path": "setup.cfg",
    "content": "[pycodestyle]\nignore = E701\nmax-line-length = 100\n\n[pylint.checkers]\nmax-line-length = 100\nmax-args = 8\nmax-attributes = 12\n\n[pylint.messages_control]\ndisable = E0402,R0903,W0236\n"
  },
  {
    "path": "setup.py",
    "content": "import setuptools\n\nwith open(\"README.md\", \"r\", encoding=\"utf-8\") as fh:\n    long_description = fh.read()\n\nsetuptools.setup(\n    name=\"unicaps\",\n    version=\"1.3.0\",\n    author=\"Sergey Scat\",\n    author_email=\"py.unicaps@gmail.com\",\n    description=\"Universal CAPTCHA Solver for humans\",\n    long_description=long_description,\n    long_description_content_type=\"text/markdown\",\n    url=\"https://github.com/sergey-scat/unicaps\",\n    packages=setuptools.find_packages(),\n    install_requires=[\"httpx>=0.22.0\", \"enforce-typing>=1.0.0\"],\n    classifiers=[\n        \"Development Status :: 5 - Production/Stable\",\n        \"Intended Audience :: Developers\",\n        \"License :: OSI Approved :: Apache Software License\",\n        \"Operating System :: OS Independent\",\n        \"Programming Language :: Python :: 3\",\n        \"Programming Language :: Python :: 3.7\",\n        \"Programming Language :: Python :: 3.8\",\n        \"Programming Language :: Python :: 3.9\",\n        \"Programming Language :: Python :: 3.10\",\n        \"Programming Language :: Python :: 3.11\"\n    ],\n    python_requires='>=3.7',\n)\n"
  },
  {
    "path": "tests/_helpers.py",
    "content": "\"\"\"\nHelpers\n\"\"\"\n\nimport collections.abc\n\n\ndef dict_update(src_dict, upd_dict):\n    \"\"\" Updates nested dict recursively \"\"\"\n\n    for key, value in upd_dict.items():\n        if isinstance(value, collections.abc.Mapping):\n            src_dict[key] = dict_update(src_dict.get(key, {}), value)\n        else:\n            src_dict[key] = value\n    return src_dict\n"
  },
  {
    "path": "tests/conftest.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport enum\nimport importlib\nimport os.path\nimport random\nimport string\nimport sys\n\nfrom pytest import fixture\n\ncd = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))\nsys.path.insert(1, cd)\nfrom unicaps._captcha import CaptchaType\n\n\n@fixture()\ndef services():\n    pass\n\n\n@fixture()\ndef captcha_service():\n    pass\n\n\ndef _gen_random_word(length):\n    letters = string.ascii_lowercase\n    return ''.join(random.choice(letters) for i in range(length))\n\n\ndef _get_random_value(field):\n    cls = getattr(field.type, '__args__', [field.type])[0]\n\n    if cls is str:\n        return _gen_random_word(10)\n    if cls is bytes:\n        return _gen_random_word(32).encode('utf-8')\n    if cls is int:\n        return random.randint(1, 255)\n    if cls is bool:\n        return bool(random.randint(0, 1))\n    if issubclass(cls, enum.Enum):\n        return getattr(cls, cls._member_names_[0])\n\n    return cls()\n\n\n@fixture(scope=\"module\", params=CaptchaType)\ndef captcha_class(request):\n    captcha_package = importlib.import_module('unicaps.captcha')\n    return getattr(captcha_package, request.param.value)\n\n\n@fixture(scope=\"module\")\ndef captcha_instance(captcha_class):\n    # return random_dataclass_init(captcha_class)\n    params = captcha_class.__dataclass_fields__.copy()\n    for name, field in params.items():\n        params[name] = _get_random_value(field)\n\n        # for ImageCaptcha\n        if name == 'image':\n            params[name] = params[name][:6] + b'Exif' + params[name][10:]\n\n    return captcha_class(**params)\n"
  },
  {
    "path": "tests/data/__init__.py",
    "content": ""
  },
  {
    "path": "tests/data/data.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nTest data\n\"\"\"\n\nimport base64\nimport json\nimport os.path\nimport pathlib\nfrom random import choice\nfrom unittest import mock\n\nfrom unicaps import CaptchaSolvingService, exceptions as exc  # type: ignore\nfrom unicaps.captcha import (  # type: ignore\n    CaptchaType, ImageCaptcha, RecaptchaV2, RecaptchaV3, FunCaptcha, TextCaptcha,\n    KeyCaptcha, GeeTest, GeeTestV4, HCaptcha, CapyPuzzle, TikTokCaptcha\n)\nfrom unicaps.common import CaptchaAlphabet, CaptchaCharType, WorkerLanguage  # type: ignore\nfrom unicaps.proxy import ProxyServer  # type: ignore\n\nCURRENT_DIR = os.path.dirname(os.path.abspath(__file__))\n\nAPI_KEY = 'test'\n\nIMAGE_FILE_PATH = os.path.join(CURRENT_DIR, 'image.jpg')\nIMAGE_FILE_PATHLIB = pathlib.Path(IMAGE_FILE_PATH)\nIMAGE_FILE_FILEOBJECT = open(IMAGE_FILE_PATH, 'rb')\nwith open(IMAGE_FILE_PATH, 'rb') as f:\n    IMAGE_FILE_BYTES = f.read()\nIMAGE_FILE_BASE64 = base64.b64encode(IMAGE_FILE_BYTES)\nIMAGE_FILE_BASE64_STR = IMAGE_FILE_BASE64.decode('ascii')\n\nPROXY_ADDRESS = 'http://login:password@example.com:8080'\nPROXY_OBJ = ProxyServer(PROXY_ADDRESS)\nPROXY_TYPE = 'HTTP'\n\nCOOKIES = {'cookie1': 'value1', 'cookie2': 'value2'}\n\nINPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC = {\n    1: (ImageCaptcha(IMAGE_FILE_BYTES), None, None, None),\n    2: (ImageCaptcha(IMAGE_FILE_PATHLIB), None, None, None),\n    3: (ImageCaptcha(IMAGE_FILE_FILEOBJECT, is_phrase=True), None, None, None),\n    4: (ImageCaptcha(IMAGE_FILE_BYTES, is_case_sensitive=True), None, None, None),\n    5: (ImageCaptcha(IMAGE_FILE_BYTES, char_type=CaptchaCharType.ALPHA), None, None, None),\n    6: (ImageCaptcha(IMAGE_FILE_BYTES, is_math=True), None, None, None),\n    7: (ImageCaptcha(IMAGE_FILE_BYTES, min_len=1), None, None, None),\n    8: (ImageCaptcha(IMAGE_FILE_BYTES, max_len=10), None, None, None),\n    9: (ImageCaptcha(IMAGE_FILE_BYTES, alphabet=CaptchaAlphabet.LATIN), None, None, None),\n    10: (ImageCaptcha(IMAGE_FILE_BYTES, language=WorkerLanguage.ENGLISH), None, None, None),\n    11: (ImageCaptcha(IMAGE_FILE_BYTES, comment='test'), None, None, None),\n    12: (RecaptchaV2('test1', 'test2'), None, None, None),\n    13: (RecaptchaV2('test1', 'test2', is_invisible=True), None, None, None),\n    14: (RecaptchaV2('test1', 'test2', data_s='test3'), None, None, None),\n    15: (RecaptchaV3('test1', 'test2'), None, None, None),\n    16: (RecaptchaV3('test1', 'test2', action='test3'), None, None, None),\n    17: (RecaptchaV3('test1', 'test2', min_score=0.9), None, None, None),\n    18: (FunCaptcha('test1', 'test2'), None, None, None),\n    19: (FunCaptcha('test1', 'test2', service_url='test3'), None, None, None),\n    20: (FunCaptcha('test1', 'test2', no_js=True), None, None, None),\n    21: (TextCaptcha('test1'), None, None, None),\n    22: (TextCaptcha('test1', alphabet=CaptchaAlphabet.LATIN), None, None, None),\n    23: (TextCaptcha('test1', language=WorkerLanguage.ENGLISH), None, None, None),\n    24: (KeyCaptcha('test1', 'test2', 'test3', 'test4', 'test5'), None, None, None),\n    25: (GeeTest('test1', 'test2', 'test3'), None, None, None),\n    26: (GeeTest('test1', 'test2', 'test3', api_server='test4'), None, None, None),\n    27: (HCaptcha('test1', 'test2'), None, None, None),\n    28: (CapyPuzzle('test1', 'test2', 'test3'), None, None, None),\n    29: (TikTokCaptcha('test1'), None, None, COOKIES),\n    30: (RecaptchaV2('test1', 'test2'), PROXY_OBJ, None, None),\n    31: (RecaptchaV2('test1', 'test2'), PROXY_OBJ, 'User-Agent1', None),\n    32: (RecaptchaV2('test1', 'test2'), PROXY_OBJ, 'User-Agent1', COOKIES),\n    33: (RecaptchaV2('test1', 'test2', data_s=\"test3\", is_enterprise=True), None, None, None),\n    34: (RecaptchaV3('test1', 'test2', is_enterprise=True), None, None, None),\n    35: (TikTokCaptcha('test1', aid=1459), None, None, COOKIES),\n    36: (TikTokCaptcha('test1', host='test2'), None, None, COOKIES),\n    37: (TikTokCaptcha('https://www.tiktok.com/login/phone-or-email/email'), None, None, COOKIES),\n    38: (TikTokCaptcha('https://ads.tiktok.com/i18n/signup'), None, None, COOKIES),\n    39: (GeeTestV4('test1', 'test2'), None, None, None),\n    40: (RecaptchaV2('test1', 'test2', api_domain='recaptcha.net'), None, None, None),\n    41: (RecaptchaV3('test1', 'test2', api_domain='recaptcha.net'), None, None, None),\n    42: (FunCaptcha('test1', 'test2', blob='test3'), None, None, None),\n}\n\nBASE_TASK_REQUEST_DATA = {\n    CaptchaSolvingService.TWOCAPTCHA: dict(\n        method='POST',\n        url='https://2captcha.com/in.php',\n        headers={'Accept': 'application/json'},\n        data=dict(key=API_KEY, json=1, soft_id=2738)\n    ),\n    CaptchaSolvingService.RUCAPTCHA: dict(\n        method='POST',\n        url='https://rucaptcha.com/in.php',\n        headers={'Accept': 'application/json'},\n        data=dict(key=API_KEY, json=1, soft_id=2738)\n    ),\n    CaptchaSolvingService.ANTI_CAPTCHA: dict(\n        method='POST',\n        json=dict(clientKey=API_KEY, softId=940),\n        headers={'Accept': 'application/json'},\n        url='https://api.anti-captcha.com/createTask'\n    ),\n    CaptchaSolvingService.AZCAPTCHA: dict(\n        method='POST',\n        url='http://azcaptcha.com/in.php',\n        data=dict(key=API_KEY, json=1)\n    ),\n    CaptchaSolvingService.CPTCH_NET: dict(\n        method='POST',\n        url='https://cptch.net/in.php',\n        data=dict(key=API_KEY, json=1, soft_id=\"164\")\n    ),\n    CaptchaSolvingService.DEATHBYCAPTCHA: dict(\n        method='POST',\n        headers={'Accept': 'application/json'},\n        url='http://api.dbcapi.me/api/captcha',\n        data=dict(authtoken=API_KEY)\n    )\n}\n\nOUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC = {\n    CaptchaSolvingService.TWOCAPTCHA: {\n        1: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},\n        2: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},\n        3: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, phrase=1)},\n        4: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, regsense=1)},\n        5: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, numeric=2)},\n        6: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, calc=1)},\n        7: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, min_len=1)},\n        8: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, max_len=10)},\n        9: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, language=2)},\n        10: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, lang='en')},\n        11: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, textinstructions='test')},\n        12: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},\n        13: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=1)},\n        14: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',\n                      'invisible': 0, 'data-s': 'test3'}},\n        15: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',\n                          pageurl='test2')},\n        16: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          action='test3')},\n        17: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          min_score=0.9)},\n        18: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2')},\n        19: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2', surl='test3')},\n        20: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2', nojs=1)},\n        21: {'data': dict(textcaptcha='test1')},\n        22: {'data': dict(textcaptcha='test1', language=2)},\n        23: {'data': dict(textcaptcha='test1', lang='en')},\n        24: {'data': dict(method='keycaptcha', pageurl='test1', s_s_c_user_id='test2',\n                          s_s_c_session_id='test3', s_s_c_web_server_sign='test4',\n                          s_s_c_web_server_sign2='test5')},\n        25: {'data': dict(method='geetest', pageurl='test1', gt='test2', challenge='test3')},\n        26: {'data': dict(method='geetest', pageurl='test1', gt='test2', challenge='test3',\n                          api_server='test4')},\n        27: {'data': dict(method='hcaptcha', sitekey='test1', pageurl='test2', invisible=0)},\n        28: {'data': dict(method='capy', captchakey='test1', pageurl='test2', api_server='test3')},\n        29: {'data': dict(method='tiktok', pageurl='test1', aid=None, host=None,\n                          cookies='cookie1:value1;cookie2:value2')},\n        30: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,\n                          proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],\n                          proxytype=PROXY_TYPE)},\n        31: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,\n                          proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],\n                          proxytype=PROXY_TYPE,\n                          userAgent='User-Agent1')},\n        32: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,\n                          proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],\n                          proxytype=PROXY_TYPE,\n                          userAgent='User-Agent1',\n                          cookies='cookie1:value1;cookie2:value2')},\n        33: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',\n                      'invisible': 0, 'data-s': 'test3', 'enterprise': 1}},\n        34: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          enterprise=1)},\n        35: {'data': dict(method='tiktok', pageurl='test1', aid=1459, host=None,\n                          cookies='cookie1:value1;cookie2:value2')},\n        36: {'data': dict(method='tiktok', pageurl='test1', aid=None, host='test2',\n                          cookies='cookie1:value1;cookie2:value2')},\n        37: {'data': dict(method='tiktok',\n                          pageurl='https://www.tiktok.com/login/phone-or-email/email',\n                          aid=1459, host='https://www-useast1a.tiktok.com',\n                          cookies='cookie1:value1;cookie2:value2')},\n        38: {'data': dict(method='tiktok', pageurl='https://ads.tiktok.com/i18n/signup',\n                          aid=1583, host='https://verify-sg.byteoversea.com',\n                          cookies='cookie1:value1;cookie2:value2')},\n        39: {'data': dict(method='geetest_v4 ', pageurl='test1', captcha_id='test2')},\n        40: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2',\n                          invisible=0, domain='recaptcha.net')},\n        41: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          domain='recaptcha.net')},\n        42: {'data': {'method': 'funcaptcha', 'publickey': 'test1', 'pageurl': 'test2',\n                      'data[blob]': 'test3'}},\n    },\n    CaptchaSolvingService.ANTI_CAPTCHA: {\n        1: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR))},\n        2: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR))},\n        3: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,\n                                   phrase=True))},\n        4: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR, case=True))},\n        5: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR, numeric=2))},\n        6: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR, math=True))},\n        7: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,\n                                   minLength=1))},\n        8: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,\n                                   maxLength=10))},\n        9: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR))},\n        10: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR),\n                          languagePool='en')},\n        11: {'json': dict(task=dict(type='ImageToTextTask', body=IMAGE_FILE_BASE64_STR,\n                                    comment='test'))},\n        12: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False))},\n        13: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=True))},\n        14: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False,\n                                    recaptchaDataSValue='test3'))},\n        15: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',\n                                    websiteURL='test2'))},\n        16: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', pageAction='test3'))},\n        17: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', minScore=0.9))},\n        18: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',\n                                    websiteURL='test2'))},\n        19: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',\n                                    websiteURL='test2', funcaptchaApiJSSubdomain='test3'))},\n        20: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',\n                                    websiteURL='test2'))},\n        21: None,\n        22: None,\n        23: None,\n        24: None,\n        25: {'json': dict(task=dict(type='GeeTestTaskProxyless', websiteURL='test1',\n                                    gt='test2', challenge='test3'))},\n        26: {'json': dict(task=dict(type='GeeTestTaskProxyless', websiteURL='test1',\n                                    gt='test2', challenge='test3',\n                                    geetestApiServerSubdomain='test4'))},\n        27: {'json': dict(task=dict(type='HCaptchaTaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False))},\n        28: None,\n        29: None,\n        30: {'json': dict(task=dict(type='NoCaptchaTask', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False,\n                                    proxyType=PROXY_OBJ.proxy_type.value,\n                                    proxyAddress=PROXY_OBJ.get_ip_address(),\n                                    proxyPort=PROXY_OBJ.port,\n                                    proxyLogin=PROXY_OBJ.login,\n                                    proxyPassword=PROXY_OBJ.password))},\n        31: {'json': dict(task=dict(type='NoCaptchaTask', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False,\n                                    proxyType=PROXY_OBJ.proxy_type.value,\n                                    proxyAddress=PROXY_OBJ.get_ip_address(),\n                                    proxyPort=PROXY_OBJ.port,\n                                    proxyLogin=PROXY_OBJ.login,\n                                    proxyPassword=PROXY_OBJ.password,\n                                    userAgent='User-Agent1'))},\n        32: {'json': dict(task=dict(type='NoCaptchaTask', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False,\n                                    proxyType=PROXY_OBJ.proxy_type.value,\n                                    proxyAddress=PROXY_OBJ.get_ip_address(),\n                                    proxyPort=PROXY_OBJ.port,\n                                    proxyLogin=PROXY_OBJ.login,\n                                    proxyPassword=PROXY_OBJ.password,\n                                    userAgent='User-Agent1',\n                                    cookies='cookie1=value1; cookie2=value2'))},\n        33: {'json': dict(task=dict(type='RecaptchaV2EnterpriseTaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False,\n                                    enterprisePayload=dict(s='test3')))},\n        34: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', isEnterprise=True))},\n        35: None,\n        36: None,\n        37: None,\n        38: None,\n        39: {'json': dict(task=dict(type='GeeTestTaskProxyless', websiteURL='test1',\n                                    gt='test2', version=4))},\n        40: {'json': dict(task=dict(type='NoCaptchaTaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', isInvisible=False,\n                                    apiDomain='recaptcha.net'))},\n        41: {'json': dict(task=dict(type='RecaptchaV3TaskProxyless', websiteKey='test1',\n                                    websiteURL='test2', apiDomain='recaptcha.net'))},\n        42: {'json': dict(task=dict(type='FunCaptchaTaskProxyless', websitePublicKey='test1',\n                                    websiteURL='test2', data='{\"blob\": \"test3\"}'))},\n    },\n    CaptchaSolvingService.AZCAPTCHA: {\n        1: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},\n        2: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},\n        3: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, phrase=1)},\n        4: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, regsense=1)},\n        5: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, numeric=2)},\n        6: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, calc=1)},\n        7: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, min_len=1)},\n        8: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, max_len=10)},\n        9: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, language=2)},\n        10: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, lang='en')},\n        11: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, textinstructions='test')},\n        12: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},\n        13: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=1)},\n        14: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',\n                      'invisible': 0, 'data-s': 'test3'}},\n        15: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',\n                          pageurl='test2')},\n        16: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          action='test3')},\n        17: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          min_score=0.9)},\n        18: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2')},\n        19: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2', surl='test3')},\n        20: {'data': dict(method='funcaptcha', publickey='test1', pageurl='test2')},\n        21: None,\n        22: None,\n        23: None,\n        24: None,\n        25: None,\n        26: None,\n        27: {'data': dict(method='hcaptcha', sitekey='test1', pageurl='test2')},\n        28: None,\n        29: None,\n        30: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,\n                          proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],\n                          proxytype=PROXY_TYPE)},\n        31: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,\n                          proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],\n                          proxytype=PROXY_TYPE,\n                          userAgent='User-Agent1')},\n        32: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0,\n                          proxy=PROXY_ADDRESS.split('://', maxsplit=1)[1],\n                          proxytype=PROXY_TYPE,\n                          userAgent='User-Agent1',\n                          cookies='cookie1:value1;cookie2:value2')},\n        33: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',\n                      'invisible': 0, 'data-s': 'test3'}},\n        34: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',\n                          pageurl='test2')},\n        35: None,\n        36: None,\n        37: None,\n        38: None,\n        39: None,\n        40: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',\n                      'invisible': 0}},\n        41: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',\n                          pageurl='test2')},\n        42: {'data': {'method': 'funcaptcha', 'publickey': 'test1', 'pageurl': 'test2',\n                      'data[blob]': 'test3'}},\n    },\n    CaptchaSolvingService.CPTCH_NET: {\n        1: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},\n        2: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR)},\n        3: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, phrase=1)},\n        4: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, regsense=1)},\n        5: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, numeric=2)},\n        6: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, calc=1)},\n        7: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, min_len=1)},\n        8: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, max_len=10)},\n        9: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, language=2)},\n        10: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, lang='en')},\n        11: {'data': dict(method='base64', body=IMAGE_FILE_BASE64_STR, textinstructions='test')},\n        12: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},\n        13: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=1)},\n        14: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',\n                      'invisible': 0}},\n        15: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',\n                          pageurl='test2')},\n        16: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          action='test3')},\n        17: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1', pageurl='test2',\n                          min_score=0.9)},\n        18: None,\n        19: None,\n        20: None,\n        21: None,\n        22: None,\n        23: None,\n        24: None,\n        25: None,\n        26: None,\n        27: None,\n        28: None,\n        29: None,\n        30: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},\n        31: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},\n        32: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},\n        33: {'data': dict(method='userrecaptcha', googlekey='test1', pageurl='test2', invisible=0)},\n        34: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',\n                          pageurl='test2')},\n        35: None,\n        36: None,\n        37: None,\n        38: None,\n        39: None,\n        40: {'data': {'method': 'userrecaptcha', 'googlekey': 'test1', 'pageurl': 'test2',\n                      'invisible': 0}},\n        41: {'data': dict(method='userrecaptcha', version='v3', googlekey='test1',\n                          pageurl='test2')},\n        42: None,\n    },\n    CaptchaSolvingService.DEATHBYCAPTCHA: {\n        1: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        2: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        3: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        4: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        5: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        6: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        7: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        8: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        9: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        10: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        11: {'data': dict(captchafile='base64:' + IMAGE_FILE_BASE64_STR)},\n        12: {'data': dict(type=4,\n                          token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2'}))},\n        13: {'data': dict(type=4,\n                          token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2'}))},\n        14: {'data': dict(type=4, token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2',\n                                                           'data-s': 'test3'}))},\n        15: {'data': dict(type=5,\n                          token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2'}))},\n        16: {'data': dict(type=5, token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2',\n                                                           'action': 'test3'}))},\n        17: {'data': dict(type=5, token_params=json.dumps({'googlekey': 'test1', 'pageurl': 'test2',\n                                                           'min_score': 0.9}))},\n        18: {'data': dict(type=6,\n                          funcaptcha_params=json.dumps({'publickey': 'test1',\n                                                        'pageurl': 'test2'}))},\n        19: {'data': dict(type=6,\n                          funcaptcha_params=json.dumps({'publickey': 'test1',\n                                                        'pageurl': 'test2'}))},\n        20: {'data': dict(type=6,\n                          funcaptcha_params=json.dumps({'publickey': 'test1',\n                                                        'pageurl': 'test2'}))},\n        21: None,\n        22: None,\n        23: None,\n        24: None,\n        25: None,\n        26: None,\n        27: {'data': dict(type=7,\n                          hcaptcha_params=json.dumps({'sitekey': 'test1', 'pageurl': 'test2'}))},\n        28: None,\n        29: None,\n        30: {'data': dict(\n            type=4,\n            token_params=json.dumps(\n                dict(\n                    googlekey='test1',\n                    pageurl='test2',\n                    proxy=PROXY_ADDRESS,\n                    proxytype=PROXY_TYPE\n                )\n            )\n        )},\n        31: {'data': dict(\n            type=4,\n            token_params=json.dumps(\n                dict(\n                    googlekey='test1',\n                    pageurl='test2',\n                    proxy=PROXY_ADDRESS,\n                    proxytype=PROXY_TYPE\n                )\n            )\n        )},\n        32: {'data': dict(\n            type=4,\n            token_params=json.dumps(\n                dict(\n                    googlekey='test1',\n                    pageurl='test2',\n                    proxy=PROXY_ADDRESS,\n                    proxytype=PROXY_TYPE\n                )\n            )\n        )},\n        33: {'data': dict(\n            type=4,\n            token_params=json.dumps(\n                {'googlekey': 'test1', 'pageurl': 'test2', 'data-s': 'test3'}\n            )\n        )},\n        34: {'data': dict(\n            type=5,\n            token_params=json.dumps(\n                dict(googlekey='test1', pageurl='test2')\n            )\n        )},\n        35: None,\n        36: None,\n        37: None,\n        38: None,\n        39: None,\n        40: {'data': dict(\n            type=4,\n            token_params=json.dumps(\n                dict(googlekey='test1', pageurl='test2')\n            )\n        )},\n        41: {'data': dict(\n            type=5,\n            token_params=json.dumps(\n                dict(googlekey='test1', pageurl='test2')\n            )\n        )},\n        42: {'data': dict(\n            type=6,\n            funcaptcha_params=json.dumps(\n                dict(publickey='test1', pageurl='test2')\n            )\n        )},\n    }\n}\nOUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC[CaptchaSolvingService.RUCAPTCHA] = (\n    OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC[CaptchaSolvingService.TWOCAPTCHA]\n)\n\n\ndef get_http_resp_obj(ret_value, status_code=200, reason_phrase='OK', is_success=True,\n                      is_error=False):\n    \"\"\" Mocked response object \"\"\"\n    obj = mock.Mock()\n    obj.json = ret_value.copy\n    obj.status_code = status_code\n    obj.reason_phrase = reason_phrase\n    obj.is_success = is_success\n    obj.is_error = is_error\n    return obj\n\n\nINPUT_TEST_LIST_FOR_TASK_PARSE_RESPONSE_FUNC = {\n    1: CaptchaType.IMAGE,\n    2: CaptchaType.RECAPTCHAV2,\n    3: CaptchaType.RECAPTCHAV3,\n    4: CaptchaType.TEXT,\n    5: CaptchaType.FUNCAPTCHA,\n    6: CaptchaType.KEYCAPTCHA,\n    7: CaptchaType.GEETEST,\n    8: CaptchaType.HCAPTCHA,\n    9: CaptchaType.CAPY,\n    10: CaptchaType.TIKTOK,\n    11: CaptchaType.GEETESTV4\n}\n\nINPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC = {\n    CaptchaSolvingService.TWOCAPTCHA: {\n        1: get_http_resp_obj(dict(status=1, request='1234567890')),\n        2: get_http_resp_obj(dict(status=1, request='1234567890')),\n        3: get_http_resp_obj(dict(status=1, request='1234567890')),\n        4: get_http_resp_obj(dict(status=1, request='1234567890')),\n        5: get_http_resp_obj(dict(status=1, request='1234567890')),\n        6: get_http_resp_obj(dict(status=1, request='1234567890')),\n        7: get_http_resp_obj(dict(status=1, request='1234567890')),\n        8: get_http_resp_obj(dict(status=1, request='1234567890')),\n        9: get_http_resp_obj(dict(status=1, request='1234567890')),\n        10: get_http_resp_obj(dict(status=1, request='1234567890')),\n        11: get_http_resp_obj(dict(status=1, request='1234567890')),\n    },\n    CaptchaSolvingService.ANTI_CAPTCHA: {\n        1: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),\n        2: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),\n        3: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),\n        4: None,\n        5: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),\n        6: None,\n        7: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),\n        8: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),\n        9: None,\n        10: None,\n        11: get_http_resp_obj(dict(errorId=0, taskId='1234567890')),\n    },\n    CaptchaSolvingService.AZCAPTCHA: {\n        1: get_http_resp_obj(dict(status=1, request='1234567890')),\n        2: get_http_resp_obj(dict(status=1, request='1234567890')),\n        3: get_http_resp_obj(dict(status=1, request='1234567890')),\n        4: None,\n        5: get_http_resp_obj(dict(status=1, request='1234567890')),\n        6: None,\n        7: None,\n        8: get_http_resp_obj(dict(status=1, request='1234567890')),\n        9: None,\n        10: None,\n        11: None,\n    },\n    CaptchaSolvingService.CPTCH_NET: {\n        1: get_http_resp_obj(dict(status=1, request='1234567890')),\n        2: get_http_resp_obj(dict(status=1, request='1234567890')),\n        3: get_http_resp_obj(dict(status=1, request='1234567890')),\n        4: None,\n        5: None,\n        6: None,\n        7: None,\n        8: None,\n        9: None,\n        10: None,\n        11: None,\n    },\n    CaptchaSolvingService.DEATHBYCAPTCHA: {\n        1: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),\n        2: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),\n        3: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),\n        4: None,\n        5: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),\n        6: None,\n        7: None,\n        8: get_http_resp_obj(dict(status=0, captcha='1234567890', is_correct=True, text='test')),\n        9: None,\n        10: None,\n        11: None,\n    }\n}\nINPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.RUCAPTCHA] = (\n    INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.TWOCAPTCHA]\n)\n\nOUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC = {\n    CaptchaSolvingService.TWOCAPTCHA: {\n        1: dict(task_id='1234567890', extra={}),\n        2: dict(task_id='1234567890', extra={}),\n        3: dict(task_id='1234567890', extra={}),\n        4: dict(task_id='1234567890', extra={}),\n        5: dict(task_id='1234567890', extra={}),\n        6: dict(task_id='1234567890', extra={}),\n        7: dict(task_id='1234567890', extra={}),\n        8: dict(task_id='1234567890', extra={}),\n        9: dict(task_id='1234567890', extra={}),\n        10: dict(task_id='1234567890', extra={}),\n        11: dict(task_id='1234567890', extra={}),\n    },\n    CaptchaSolvingService.ANTI_CAPTCHA: {\n        1: dict(task_id='1234567890', extra={}),\n        2: dict(task_id='1234567890', extra={}),\n        3: dict(task_id='1234567890', extra={}),\n        4: None,\n        5: dict(task_id='1234567890', extra={}),\n        6: None,\n        7: dict(task_id='1234567890', extra={}),\n        8: dict(task_id='1234567890', extra={}),\n        9: None,\n        10: None,\n        11: dict(task_id='1234567890', extra={}),\n    },\n    CaptchaSolvingService.AZCAPTCHA: {\n        1: dict(task_id='1234567890', extra={}),\n        2: dict(task_id='1234567890', extra={}),\n        3: dict(task_id='1234567890', extra={}),\n        4: None,\n        5: dict(task_id='1234567890', extra={}),\n        6: None,\n        7: None,\n        8: dict(task_id='1234567890', extra={}),\n        9: None,\n        10: None,\n        11: None,\n    },\n    CaptchaSolvingService.CPTCH_NET: {\n        1: dict(task_id='1234567890', extra={}),\n        2: dict(task_id='1234567890', extra={}),\n        3: dict(task_id='1234567890', extra={}),\n        4: None,\n        5: None,\n        6: None,\n        7: None,\n        8: None,\n        9: None,\n        10: None,\n        11: None,\n    },\n    CaptchaSolvingService.DEATHBYCAPTCHA: {\n        1: dict(task_id='1234567890', extra={}),\n        2: dict(task_id='1234567890', extra={}),\n        3: dict(task_id='1234567890', extra={}),\n        4: None,\n        5: dict(task_id='1234567890', extra={}),\n        6: None,\n        7: None,\n        8: dict(task_id='1234567890', extra={}),\n        9: None,\n        10: None,\n        11: None,\n    }\n}\nOUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.RUCAPTCHA] = (\n    OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[CaptchaSolvingService.TWOCAPTCHA]\n)\n\nOUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC = {\n    1: exc.ServiceError,\n    2: exc.AccessDeniedError,\n    3: exc.LowBalanceError,\n    4: exc.ServiceTooBusy,\n    5: exc.TooManyRequestsError,\n    6: exc.MalformedRequestError,\n    7: exc.BadInputDataError,\n    8: exc.UnableToSolveError,\n    9: exc.ProxyError\n}\n\nINPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC = {\n    CaptchaSolvingService.TWOCAPTCHA: {\n        1: get_http_resp_obj(dict(status=0, request=choice(\n            ['REPORT_NOT_RECORDED', 'ERROR_IP_ADDRES']))),\n        2: get_http_resp_obj(dict(status=0, request=choice(\n            ['ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST', 'ERROR_IP_NOT_ALLOWED',\n             'IP_BANNED']))),\n        3: get_http_resp_obj(dict(status=0, request='ERROR_ZERO_BALANCE')),\n        4: get_http_resp_obj(dict(status=0, request='ERROR_NO_SLOT_AVAILABLE')),\n        5: get_http_resp_obj(dict(status=0, request=choice(\n            ['MAX_USER_TURN', 'ERROR: 1001', 'ERROR: 1005']))),\n        6: get_http_resp_obj(dict(status=0, request=choice(\n            ['ERROR_WRONG_ID_FORMAT', 'ERROR_WRONG_CAPTCHA_ID']))),\n        7: get_http_resp_obj(dict(status=0, request=choice(\n            'ERROR_UPLOAD ERROR_ZERO_CAPTCHA_FILESIZE ERROR_TOO_BIG_CAPTCHA_FILESIZE '\n            'ERROR_WRONG_FILE_EXTENSION ERROR_IMAGE_TYPE_NOT_SUPPORTED ERROR_PAGEURL '\n            'ERROR_BAD_TOKEN_OR_PAGEURL ERROR_GOOGLEKEY ERROR_BAD_PARAMETERS '\n            'ERROR_TOKEN_EXPIRED ERROR_EMPTY_ACTION'.split()))),\n        8: get_http_resp_obj(dict(status=0, request=choice(\n            'ERROR_CAPTCHAIMAGE_BLOCKED ERROR_CAPTCHA_UNSOLVABLE ERROR_BAD_DUPLICATES'.split()))),\n        9: get_http_resp_obj(dict(status=0, request=choice(\n            ['ERROR_BAD_PROXY', 'ERROR_PROXY_CONNECTION_FAILED']))),\n    },\n    CaptchaSolvingService.ANTI_CAPTCHA: {\n        1: get_http_resp_obj(dict(errorId=2, errorCode='UNKNOWN_ERROR')),\n        2: get_http_resp_obj(dict(errorId=3, errorCode=choice(\n            ['ERROR_KEY_DOES_NOT_EXIST', 'ERROR_IP_NOT_ALLOWED', 'ERROR_IP_BLOCKED']))),\n        3: get_http_resp_obj(dict(errorId=4, errorCode='ERROR_ZERO_BALANCE')),\n        4: get_http_resp_obj(dict(errorId=4, errorCode='ERROR_NO_SLOT_AVAILABLE')),\n        5: None,\n        6: get_http_resp_obj(dict(errorId=4, errorCode=choice(\n            'ERROR_NO_SUCH_METHOD ERROR_NO_SUCH_CAPCHA_ID ERROR_TASK_ABSENT '\n            'ERROR_TASK_NOT_SUPPORTED ERROR_FUNCAPTCHA_NOT_ALLOWED'.split()))),\n        7: get_http_resp_obj(dict(errorId=4, errorCode=choice(\n            'ERROR_ZERO_CAPTCHA_FILESIZE ERROR_TOO_BIG_CAPTCHA_FILESIZE '\n            'ERROR_IMAGE_TYPE_NOT_SUPPORTED ERROR_EMPTY_COMMENT ERROR_INCORRECT_SESSION_DATA '\n            'ERROR_RECAPTCHA_INVALID_SITEKEY ERROR_RECAPTCHA_INVALID_DOMAIN '\n            'ERROR_RECAPTCHA_OLD_BROWSER ERROR_TOKEN_EXPIRED ERROR_INVISIBLE_RECAPTCHA'.split()))),\n        8: get_http_resp_obj(dict(errorId=4, errorCode=choice(\n            'ERROR_CAPTCHA_UNSOLVABLE ERROR_BAD_DUPLICATES ERROR_RECAPTCHA_TIMEOUT '\n            'ERROR_FAILED_LOADING_WIDGET'.split()))),\n        9: get_http_resp_obj(dict(errorId=4, errorCode=choice(\n            'ERROR_PROXY_CONNECT_REFUSED ERROR_PROXY_CONNECT_TIMEOUT ERROR_PROXY_READ_TIMEOUT '\n            'ERROR_PROXY_BANNED ERROR_PROXY_TRANSPARENT ERROR_PROXY_HAS_NO_IMAGE_SUPPORT '\n            'ERROR_PROXY_INCOMPATIBLE_HTTP_VERSION ERROR_PROXY_NOT_AUTHORISED'.split()))),\n    },\n    CaptchaSolvingService.CPTCH_NET: {\n        1: get_http_resp_obj(dict(status=0, request='UNKNOWN_ERROR')),\n        2: get_http_resp_obj(dict(status=0, request=choice(\n            ['ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST']))),\n        3: get_http_resp_obj(dict(status=0, request='ERROR_ZERO_BALANCE')),\n        4: None,\n        5: None,\n        6: get_http_resp_obj(dict(status=0, request='ERROR_WRONG_CAPTCHA_ID')),\n        7: get_http_resp_obj(dict(status=0, request=choice(\n            'ERROR_UPLOAD ERROR_ZERO_CAPTCHA_FILESIZE ERROR_TOO_BIG_CAPTCHA_FILESIZE '\n            'ERROR_PAGEURL ERROR_GOOGLEKEY ERROR'.split()))),\n        8: get_http_resp_obj(dict(status=0, request='ERROR_CAPTCHA_UNSOLVABLE')),\n        9: None,\n    },\n    CaptchaSolvingService.DEATHBYCAPTCHA: {\n        1: get_http_resp_obj(dict(status=255)),\n        2: get_http_resp_obj(dict(status=255, error=choice(\n            ['token authentication disabled', 'not-logged-in', 'banned']))),\n        3: get_http_resp_obj(dict(status=255, error='insufficient-funds')),\n        4: get_http_resp_obj(dict(status=255, error='service-overload')),\n        5: None,\n        6: get_http_resp_obj(dict(status=255, error=choice(\n            ['upload-failed', 'invalid-captcha']))),\n        7: get_http_resp_obj(dict(status=255, error=choice(\n            ['ERROR_PAGEURL', 'Invalid base64-encoded CAPTCHA', 'Not a (CAPTCHA) image',\n             'Empty CAPTCHA image', 'ERROR_GOOGLEKEY', 'ERROR_PAGEURL',\n             'ERROR_PUBLICKEY', 'ERROR_SITEKEY', 'ERROR_ACTION', 'ERROR_MIN_SCORE',\n             'ERROR_MIN_SCORE_NOT_FLOAT']))),\n        8: None,\n        9: get_http_resp_obj(dict(status=255, error=choice(\n            ['ERROR_PROXYTYPE', 'ERROR_PROXY']))),\n    }\n}\nINPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.RUCAPTCHA] = (\n    INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.TWOCAPTCHA]\n)\nINPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.AZCAPTCHA] = (\n    INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[CaptchaSolvingService.TWOCAPTCHA]\n)\n"
  },
  {
    "path": "tests/test_captcha_base.py",
    "content": "# -*- coding: UTF-8 -*-\n\nfrom unicaps._captcha import CaptchaType\nfrom unicaps._captcha.base import BaseCaptcha, BaseCaptchaSolution\n\n\ndef test_captcha_class_base(captcha_class):\n    \"\"\"\n    Checks base class of Captcha class.\n    \"\"\"\n    assert issubclass(captcha_class, BaseCaptcha)\n\n\ndef test_captcha_class_get_type(captcha_class):\n    \"\"\"\n    Checks get_type() function of Captcha class.\n    \"\"\"\n    assert isinstance(captcha_class.get_type(), CaptchaType)\n\n\ndef test_captcha_class_get_solution_class(captcha_class):\n    \"\"\"\n    Checks get_solution_class() function of Captcha class.\n    \"\"\"\n    assert issubclass(captcha_class.get_solution_class(), BaseCaptchaSolution)\n\n\ndef test_captcha_class_get_optional_data(captcha_instance):\n    \"\"\"\n    Checks get_optional_data() function of Captcha class.\n    \"\"\"\n    assert isinstance(captcha_instance.get_optional_data(), dict)\n\n\ndef test_captchasolution_class_get_type(captcha_class):\n    \"\"\"\n    Checks get_type() function of CaptchaSolution class.\n    \"\"\"\n    assert captcha_class.get_type() is captcha_class.get_solution_class().get_type()\n\n\ndef test_captchasolution_class_get_captcha_class(captcha_class):\n    \"\"\"\n    Checks get_type() function of CaptchaSolution class.\n    \"\"\"\n    assert captcha_class is captcha_class.get_solution_class().get_captcha_class()\n\n\ndef test_captchasolution_obj_get_string(captcha_class):\n    \"\"\"\n    Checks get_type() function of CaptchaSolution class.\n    \"\"\"\n    solution_class = captcha_class.get_solution_class()\n\n    dc_fields = solution_class.__dataclass_fields__\n    field_values = []\n    for i, field in enumerate(dc_fields.values(), start=1):\n        if field.type == str:\n            value = f'test{i}'\n        elif field.type == dict:\n            value = {f'test{i}': f'test{i}'}\n        else:\n            value = None\n\n        field_values.append(value)\n\n    solution_obj = solution_class(*field_values)\n\n    assert str(solution_obj) == '\\n'.join(str(v) for v in field_values)\n"
  },
  {
    "path": "tests/test_image_captcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\nimport base64\nimport os.path\nimport pathlib\n\nimport pytest\nfrom unicaps.captcha import ImageCaptcha\nfrom unicaps.exceptions import BadInputDataError\n\nDATA_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data')\nIMAGE_FILE = os.path.join(DATA_DIR, 'image.jpg')\n\n\n@pytest.fixture\ndef image_path():\n    return pathlib.Path(IMAGE_FILE)\n\n\n@pytest.fixture\ndef image_bytes():\n    return open(IMAGE_FILE, 'rb').read()\n\n\n@pytest.fixture\ndef image_base64(image_bytes):\n    return base64.b64encode(image_bytes)\n\n\ndef test_image_from_path(image_path, image_bytes):\n    \"\"\" Pathlib input test\"\"\"\n    captcha = ImageCaptcha(image=image_path)\n    assert image_bytes == captcha.get_image_bytes()\n\n\ndef test_image_from_binary(image_bytes):\n    \"\"\" Binary input test\"\"\"\n    captcha = ImageCaptcha(image=image_bytes)\n    assert image_bytes == captcha.get_image_bytes()\n\n\ndef test_not_an_image():\n    \"\"\" Not an image input test \"\"\"\n    with pytest.raises(TypeError):\n        ImageCaptcha(image='wrong_image')\n\n\ndef test_bad_image():\n    \"\"\" Bad image input test \"\"\"\n    with pytest.raises(BadInputDataError):\n        ImageCaptcha(image=b'bad_image_data')\n"
  },
  {
    "path": "tests/test_proxy.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nProxy tests\n\"\"\"\n\nfrom unicaps.proxy import ProxyServer, ProxyServerType\n\n\ndef test_proxy_from_string():\n    proxy = ProxyServer('http://login:password@address:8080')\n\n    assert proxy.address == 'address'\n    assert proxy.proxy_type == ProxyServerType.HTTP\n    assert proxy.port == 8080\n    assert proxy.login == 'login'\n    assert proxy.password == 'password'\n\n\ndef test_proxy_wo_auth_from_string():\n    proxy = ProxyServer('https://address:8080')\n\n    assert proxy.address == 'address'\n    assert proxy.proxy_type == ProxyServerType.HTTPS\n    assert proxy.port == 8080\n    assert proxy.login is None\n    assert proxy.password is None\n\n\ndef test_proxy_address_only():\n    proxy = ProxyServer('address')\n\n    assert proxy.address == 'address'\n    assert proxy.proxy_type == ProxyServerType.HTTP\n    assert proxy.port == 80\n    assert proxy.login is None\n    assert proxy.password is None\n\n\ndef test_proxy_all():\n    proxy = ProxyServer('address', ProxyServerType.SOCKS4, 80, 'login', 'password')\n\n    assert proxy.address == 'address'\n    assert proxy.proxy_type == ProxyServerType.SOCKS4\n    assert proxy.port == 80\n    assert proxy.login == 'login'\n    assert proxy.password == 'password'\n\n\ndef test_proxy_to_string():\n    proxy = ProxyServer('address', ProxyServerType.SOCKS5, 80, 'login', 'password')\n\n    assert str(proxy) == 'socks5://login:password@address:80'\n"
  },
  {
    "path": "tests/test_service_module.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nService module tests\n\"\"\"\n\nimport importlib\nimport inspect\nfrom copy import deepcopy\nfrom unittest import mock\n\nimport pytest\n\nfrom unicaps._captcha import CaptchaType\nfrom unicaps._service import SOLVING_SERVICE\n\nfrom data.data import (BASE_TASK_REQUEST_DATA, INPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC,\n                       OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC,\n                       INPUT_TEST_LIST_FOR_TASK_PARSE_RESPONSE_FUNC,\n                       INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC,\n                       OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC,\n                       INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC,\n                       OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC)\nfrom _helpers import dict_update\n\n# dict of captcha services with lists of supported captchas\nSERVICE_MODULES_FOR_TEST = {\n    'anti_captcha': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,\n                     CaptchaType.FUNCAPTCHA, CaptchaType.GEETEST, CaptchaType.GEETESTV4,\n                     CaptchaType.HCAPTCHA),\n    'twocaptcha': (CaptchaType.IMAGE, CaptchaType.TEXT, CaptchaType.RECAPTCHAV2,\n                   CaptchaType.RECAPTCHAV3, CaptchaType.FUNCAPTCHA, CaptchaType.KEYCAPTCHA,\n                   CaptchaType.GEETEST, CaptchaType.GEETESTV4, CaptchaType.HCAPTCHA,\n                   CaptchaType.CAPY, CaptchaType.TIKTOK),\n    'rucaptcha': (CaptchaType.IMAGE, CaptchaType.TEXT, CaptchaType.RECAPTCHAV2,\n                  CaptchaType.RECAPTCHAV3, CaptchaType.FUNCAPTCHA, CaptchaType.KEYCAPTCHA,\n                  CaptchaType.GEETEST, CaptchaType.GEETESTV4, CaptchaType.HCAPTCHA,\n                  CaptchaType.CAPY, CaptchaType.TIKTOK),\n    'azcaptcha': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,\n                  CaptchaType.HCAPTCHA, CaptchaType.FUNCAPTCHA),\n    'cptch_net': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3),\n    'deathbycaptcha': (CaptchaType.IMAGE, CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,\n                       CaptchaType.HCAPTCHA, CaptchaType.FUNCAPTCHA)\n}\nBASE_REQUESTS = ('GetBalance', 'GetStatus', 'ReportGood', 'ReportBad')\nTASK_REQUEST_PREPARE_PARAMS = ('self', 'captcha', 'proxy', 'user_agent', 'cookies')\nSOLUTION_REQUEST_PREPARE_PARAMS = ('self', 'task')\nREQUEST_PARSE_RESPONSE_PARAMS = ('self', 'response')\n\n\n@pytest.fixture(scope=\"module\", params=SERVICE_MODULES_FOR_TEST)\ndef service_module(request):\n    return importlib.import_module('unicaps._service.' + request.param)\n\n\n@pytest.fixture(scope=\"module\")\ndef service_instance(service_module):\n    return getattr(service_module, \"Service\")(\"test\")\n\n\n@pytest.fixture(scope=\"module\")\ndef json_response_obj():\n    def get_obj(ret_value):\n        obj = mock.Mock()\n        obj.json = lambda: ret_value\n        return obj\n    return get_obj\n\n\ndef check_if_class_is_present(module, class_name, is_not=False):\n    if is_not:\n        assert not hasattr(module, class_name), \\\n               f'{class_name} is found in service module {module.__name__}'\n    else:\n        assert hasattr(module, class_name), \\\n               f'{class_name} is not found in service module {module.__name__}'\n\n\ndef is_captcha_supported(service_module, captcha_type):\n    module_name = service_module.__name__.split('.')[-1]\n    if captcha_type in SERVICE_MODULES_FOR_TEST[module_name]:\n        return True\n    return False\n\n\ndef get_request(service_module, captcha_type, req_type='Request'):\n    request_name = captcha_type.value + req_type\n    return request_name, getattr(service_module, request_name)\n\n\ndef test_if_service_class_is_present(service_module):\n    \"\"\" Checks if Service() class is declared in the module \"\"\"\n\n    check_if_class_is_present(service_module, 'Service')\n\n\n@pytest.mark.parametrize(\"req\", BASE_REQUESTS)\ndef test_if_base_request_is_present(service_module, req):\n    \"\"\" Checks if all of the base requests are present in the module file \"\"\"\n\n    check_if_class_is_present(service_module, req + 'Request')\n\n\n@pytest.mark.parametrize(\"captcha_type\", CaptchaType)\ndef test_if_task_request_is_present(service_module, captcha_type):\n    \"\"\" Checks if all of the task requests are present in the module file \"\"\"\n\n    check_if_class_is_present(\n        service_module,\n        captcha_type.value + 'TaskRequest',\n        not is_captcha_supported(service_module, captcha_type)\n    )\n\n\n@pytest.mark.parametrize(\"captcha_type\", CaptchaType)\ndef test_if_solution_request_is_present(service_module, captcha_type):\n    \"\"\" Checks if all of the solution requests are present in the module file \"\"\"\n\n    check_if_class_is_present(\n        service_module,\n        captcha_type.value + 'SolutionRequest',\n        not is_captcha_supported(service_module, captcha_type)\n    )\n\n\n@pytest.mark.parametrize(\"req\", BASE_REQUESTS)\ndef test_base_request_signature_of_prepare_func(service_module, req):\n    \"\"\" Checks signature of the <captcha>TaskRequest.prepare() function \"\"\"\n\n    request_name = req + 'Request'\n    request_class = getattr(service_module, request_name)\n\n    if req in ('ReportGood', 'ReportBad'):\n        standard = ('self', 'solved_captcha')\n    else:\n        standard = ('self',)\n\n    params = tuple(inspect.signature(request_class.prepare).parameters)\n    assert params == standard, \\\n           f\"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}\"\n\n\n@pytest.mark.parametrize(\"captcha_type\", CaptchaType)\ndef test_task_request_signature_of_prepare_func(service_module, captcha_type):\n    \"\"\" Checks signature of the <captcha>TaskRequest.prepare() function \"\"\"\n\n    if is_captcha_supported(service_module, captcha_type):\n        request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')\n\n        params = tuple(inspect.signature(request_class.prepare).parameters)\n        assert params == TASK_REQUEST_PREPARE_PARAMS, \\\n               f\"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}\"\n\n\n@pytest.mark.parametrize(\"captcha_type\", CaptchaType)\ndef test_solution_request_signature_of_prepare_func(service_module, captcha_type):\n    \"\"\" Checks signature of the <captcha>TaskRequest.prepare() function \"\"\"\n\n    if is_captcha_supported(service_module, captcha_type):\n        request_name, request_class = get_request(service_module, captcha_type, 'SolutionRequest')\n\n        params = tuple(inspect.signature(request_class.prepare).parameters)\n        assert params == SOLUTION_REQUEST_PREPARE_PARAMS, \\\n               f\"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}\"\n\n\n@pytest.mark.parametrize(\"req\", BASE_REQUESTS)\ndef test_base_request_signature_of_parse_response_func(service_module, req):\n    \"\"\" Checks signature of the <captcha>TaskRequest.prepare() function \"\"\"\n\n    request_name = req + 'Request'\n    request_class = getattr(service_module, request_name)\n\n    params = tuple(inspect.signature(request_class.parse_response).parameters)\n    assert params == REQUEST_PARSE_RESPONSE_PARAMS, \\\n           f\"Incorrect signature of {request_name}.prepare() func: {', '.join(params)}\"\n\n\n@pytest.mark.parametrize(\"captcha_type, req_type\",\n                         [(t, r) for t in CaptchaType for r in ('TaskRequest', 'SolutionRequest')])\ndef test_captcha_request_signature_of_parse_response_func(service_module, captcha_type, req_type):\n    \"\"\"\n    Checks signature of the <captcha>TaskRequest.parse_response() and\n    <captcha>SolutionRequest.parse_response() functions.\n    \"\"\"\n\n    if not is_captcha_supported(service_module, captcha_type):\n        pytest.skip(\"CAPTCHA is not supported!\")\n\n    request_name, request_class = get_request(service_module, captcha_type, req_type)\n    params = tuple(inspect.signature(request_class.parse_response).parameters)\n    assert params == REQUEST_PARSE_RESPONSE_PARAMS, \\\n           f\"Incorrect signature of {request_name}.parse_response() func: {', '.join(params)}\"\n\n\n@pytest.mark.parametrize(\"test_id,input_data\", INPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC.items())\ndef test_task_request_return_value_of_prepare_func(service_module, test_id, input_data):\n    service_type = {v: k for k, v in SOLVING_SERVICE.items()}[service_module]\n    captcha_type = input_data[0].get_type()\n\n    if not is_captcha_supported(service_module, captcha_type):\n        pytest.skip(\"CAPTCHA is not supported!\")\n\n    request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')\n\n    # service instance\n    service = service_module.Service('test')\n\n    # request instance\n    request_instance = request_class(service)\n    result_dict = request_instance.prepare(*input_data)\n\n    standard_result = deepcopy(BASE_TASK_REQUEST_DATA[service_type])\n    dict_update(standard_result, OUTPUT_TEST_DATA_FOR_TASK_PREPARE_FUNC[service_type][test_id])\n\n    assert result_dict == standard_result\n\n\n@pytest.mark.parametrize(\"test_id,captcha_type\",\n                         INPUT_TEST_LIST_FOR_TASK_PARSE_RESPONSE_FUNC.items())\ndef test_task_request_return_value_of_parse_response_func(service_module, test_id, captcha_type):\n    service_type = {v: k for k, v in SOLVING_SERVICE.items()}[service_module]\n\n    if not is_captcha_supported(service_module, captcha_type):\n        pytest.skip(\"CAPTCHA is not supported!\")\n\n    request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')\n\n    # service instance\n    service = service_module.Service('test')\n\n    input_data = INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[service_type][test_id]\n\n    # request instance\n    request_instance = request_class(service)\n    result_dict = request_instance.parse_response(input_data)\n\n    standard_result = deepcopy(OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC[service_type][test_id])\n    assert result_dict == standard_result\n\n\n@pytest.mark.parametrize(\"test_id,captcha_type,exc_type\",\n                         [(i, c, e) for i, e in\n                          OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC.items()\n                          for c in CaptchaType])\ndef test_task_request_exception_of_parse_response_func(service_module, test_id, captcha_type,\n                                                       exc_type):\n    service_type = {v: k for k, v in SOLVING_SERVICE.items()}[service_module]\n\n    if not is_captcha_supported(service_module, captcha_type):\n        pytest.skip(\"CAPTCHA is not supported!\")\n\n    request_name, request_class = get_request(service_module, captcha_type, 'TaskRequest')\n\n    # service instance\n    service = service_module.Service('test')\n\n    # get input data, skip test\n    input_data = INPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[service_type][test_id]\n    if not input_data:\n        pytest.skip(\"The service doesn't support the testing exception!\")\n\n    # request instance\n    request_instance = request_class(service)\n\n    expected_exception = (\n        OUTPUT_TEST_DATA_FOR_TASK_PARSE_RESPONSE_FUNC_WITH_EXC[test_id]\n    )\n    with pytest.raises(expected_exception):\n        request_instance.parse_response(input_data)\n"
  },
  {
    "path": "tests/test_solver.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nCaptchaSolver tests\n\"\"\"\nfrom unittest.mock import Mock\n\nimport pytest\nfrom unicaps import CaptchaSolver, CaptchaSolvingService\nfrom unicaps.captcha import CaptchaType\n\nAPI_KEY = 'TEST_API_KEY'\n\n\n@pytest.fixture(scope=\"module\")\ndef captcha_solver():\n    return CaptchaSolver('2captcha.com', API_KEY)\n\n\n@pytest.fixture()\ndef mocked_captcha_solver(captcha_solver, monkeypatch):\n    monkeypatch.setattr(captcha_solver, '_service', Mock())\n    return captcha_solver\n\n\ndef test_solver_init():\n    service = CaptchaSolvingService.ANTI_CAPTCHA\n    solver = CaptchaSolver(service, API_KEY)\n\n    assert solver.service_name == service\n    assert solver.api_key == API_KEY\n\n\ndef test_solver_init_from_string():\n    solver = CaptchaSolver('2captcha.com', API_KEY)\n\n    assert solver.service_name == CaptchaSolvingService.TWOCAPTCHA\n    assert solver.api_key == API_KEY\n\n\ndef test_solver_bad_init():\n    with pytest.raises(ValueError):\n        CaptchaSolver('2captcha', API_KEY)\n\n\ndef test_solver_bad_init2():\n    with pytest.raises(ValueError):\n        CaptchaSolver(b'2captcha.com', API_KEY)\n\n\n# @pytest.mark.parametrize(\"captcha_type\", CaptchaType)\ndef test_call_solve_func(mocked_captcha_solver, captcha_instance):\n    mapping = {\n        CaptchaType.IMAGE: 'image_captcha',\n        CaptchaType.TEXT: 'text_captcha',\n        CaptchaType.RECAPTCHAV2: 'recaptcha_v2',\n        CaptchaType.RECAPTCHAV3: 'recaptcha_v3',\n        CaptchaType.HCAPTCHA: 'hcaptcha',\n        CaptchaType.FUNCAPTCHA: 'funcaptcha',\n        CaptchaType.KEYCAPTCHA: 'keycaptcha',\n        CaptchaType.GEETEST: 'geetest',\n        CaptchaType.CAPY: 'capy_puzzle',\n        CaptchaType.TIKTOK: 'tiktok',\n        CaptchaType.GEETESTV4: 'geetest_v4'\n    }\n\n    func = getattr(mocked_captcha_solver, f'solve_{mapping[captcha_instance.get_type()]}')\n    func(\n        **{k: v for k, v in captcha_instance.__dict__.items() if not k.startswith('_')}\n    )\n"
  },
  {
    "path": "unicaps/__init__.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nUnicaps package\n~~~~~~~~~~~~~~~\n\"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom ._solver import CaptchaSolver\nfrom ._solver_async import AsyncCaptchaSolver\nfrom ._service import CaptchaSolvingService\n\n__all__ = ('CaptchaSolver', 'AsyncCaptchaSolver', 'CaptchaSolvingService')\n"
  },
  {
    "path": "unicaps/__version__.py",
    "content": "\"\"\" Version info \"\"\"\n\n__title__ = 'unicaps'\n__description__ = 'Python CAPTCHA solving for Humans.'\n__url__ = 'https://github.com/sergey-scat/unicaps'\n__version__ = '1.3.0'\n__author__ = 'Sergey Scat'\n__license__ = 'Apache 2.0'\n__copyright__ = 'Copyright 2020-2024 Sergey Scat'\n"
  },
  {
    "path": "unicaps/_captcha/__init__.py",
    "content": "# -*- coding: UTF-8 -*-\n\n\"\"\" CAPTCHAs \"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom .image import ImageCaptcha\nfrom .text import TextCaptcha\nfrom .recaptcha_v2 import RecaptchaV2\nfrom .recaptcha_v3 import RecaptchaV3\nfrom .hcaptcha import HCaptcha\nfrom .funcaptcha import FunCaptcha\nfrom .keycaptcha import KeyCaptcha\nfrom .geetest import GeeTest\nfrom .geetest_v4 import GeeTestV4\nfrom .capy import CapyPuzzle\nfrom .tiktok import TikTokCaptcha\nfrom .base import CaptchaType\n\n__all__ = (\n    'ImageCaptcha',\n    'TextCaptcha',\n    'RecaptchaV2',\n    'RecaptchaV3',\n    'HCaptcha',\n    'FunCaptcha',\n    'KeyCaptcha',\n    'GeeTest',\n    'GeeTestV4',\n    'CapyPuzzle',\n    'TikTokCaptcha',\n    'CaptchaType'\n)\n"
  },
  {
    "path": "unicaps/_captcha/base.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nBase CAPTCHA stuff\n\"\"\"\n\nimport enum\nimport importlib\nfrom abc import ABC\nfrom dataclasses import asdict, dataclass, fields, MISSING\nfrom typing import Dict\n\n\nclass CaptchaType(enum.Enum):\n    \"\"\" Captcha type enumeration \"\"\"\n\n    IMAGE = \"ImageCaptcha\"\n    RECAPTCHAV2 = \"RecaptchaV2\"\n    RECAPTCHAV3 = \"RecaptchaV3\"\n    TEXT = \"TextCaptcha\"\n    FUNCAPTCHA = \"FunCaptcha\"\n    GEETEST = \"GeeTest\"\n    GEETESTV4 = \"GeeTestV4\"\n    HCAPTCHA = \"HCaptcha\"\n    KEYCAPTCHA = \"KeyCaptcha\"\n    CAPY = \"CapyPuzzle\"\n    TIKTOK = \"TikTokCaptcha\"\n\n\n@dataclass\nclass BaseCaptcha(ABC):\n    \"\"\" Base class for any CAPTCHA \"\"\"\n\n    @classmethod\n    def get_type(cls) -> CaptchaType:\n        \"\"\" Return CaptchaType \"\"\"\n\n        return CaptchaType(cls.__name__)\n\n    @classmethod\n    def get_solution_class(cls) -> 'BaseCaptchaSolution':\n        \"\"\" Return appropriate solution class \"\"\"\n\n        return getattr(importlib.import_module(cls.__module__), cls.__name__ + \"Solution\")\n\n    def get_optional_data(self, **kwargs) -> Dict:\n        \"\"\"\n        Return a dict with all optional fields requested (that are not None)\n        as a dict with given names.\n\n        :return: :dict:Dictionary of optional not None fields with given names and those values\n        :rtype: dict\n        \"\"\"\n\n        result = {}\n\n        if not kwargs:\n            # get all optional params\n            kwargs = {\n                field.name: (field.name, None) for field in fields(self)\n                if field.default is not MISSING\n            }\n\n        for opt_field in kwargs:\n            opt_field_value = getattr(self, opt_field)\n            field_name, converter = kwargs[opt_field]\n            if opt_field_value is not None:\n                if callable(converter):\n                    opt_field_value = converter(opt_field_value)\n                result[field_name] = opt_field_value\n        return result\n\n\n@dataclass\nclass BaseCaptchaSolution(ABC):\n    \"\"\" Base class for any CAPTCHA solution \"\"\"\n\n    @classmethod\n    def get_type(cls) -> CaptchaType:\n        \"\"\" Returns CaptchaType \"\"\"\n\n        return CaptchaType(cls.__name__.split(\"Solution\", maxsplit=1)[0])\n\n    @classmethod\n    def get_captcha_class(cls) -> BaseCaptcha:\n        \"\"\" Returns appropriate solution class \"\"\"\n\n        return getattr(\n            importlib.import_module(cls.__module__),\n            cls.__name__.split(\"Solution\", maxsplit=1)[0]\n        )\n\n    def __str__(self):\n        return '\\n'.join(str(getattr(self, field.name)) for field in fields(self))\n\n    def as_dict(self):\n        \"\"\" Get solution data as Python dictionary \"\"\"\n        return asdict(self)\n"
  },
  {
    "path": "unicaps/_captcha/capy.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nCapy Puzzle\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass CapyPuzzle(BaseCaptcha):\n    \"\"\" Capy Puzzle CAPTCHA \"\"\"\n\n    site_key: str\n    page_url: str\n    api_server: Optional[str] = None\n\n\n@enforce_types\n@dataclass\nclass CapyPuzzleSolution(BaseCaptchaSolution):\n    \"\"\" Capy Puzzle CAPTCHA solution \"\"\"\n\n    captchakey: str\n    challengekey: str\n    answer: str\n"
  },
  {
    "path": "unicaps/_captcha/funcaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nFunCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass FunCaptcha(BaseCaptcha):\n    \"\"\" FunCaptcha \"\"\"\n\n    public_key: str\n    page_url: str\n    service_url: Optional[str] = None\n    no_js: Optional[bool] = None\n    blob: Optional[str] = None\n\n\n@enforce_types\n@dataclass\nclass FunCaptchaSolution(BaseCaptchaSolution):\n    \"\"\" FunCaptcha solution \"\"\"\n\n    token: str\n"
  },
  {
    "path": "unicaps/_captcha/geetest.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nGeeTest CAPTCHA\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass GeeTest(BaseCaptcha):\n    \"\"\" GeeTest \"\"\"\n\n    page_url: str\n    gt_key: str\n    challenge: str\n    api_server: Optional[str] = None\n\n\n@enforce_types\n@dataclass\nclass GeeTestSolution(BaseCaptchaSolution):\n    \"\"\" GeeTest solution \"\"\"\n\n    challenge: str\n    validate: str\n    seccode: str\n"
  },
  {
    "path": "unicaps/_captcha/geetest_v4.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nGeeTest v4 CAPTCHA\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass GeeTestV4(BaseCaptcha):\n    \"\"\" GeeTest v4 \"\"\"\n\n    page_url: str\n    captcha_id: str\n\n\n@enforce_types\n@dataclass\nclass GeeTestV4Solution(BaseCaptchaSolution):\n    \"\"\" GeeTest v4 solution \"\"\"\n\n    captcha_id: str\n    lot_number: str\n    pass_token: str\n    gen_time: str\n    captcha_output: str\n"
  },
  {
    "path": "unicaps/_captcha/hcaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nhCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass HCaptcha(BaseCaptcha):\n    \"\"\" hCaptcha \"\"\"\n\n    site_key: str\n    page_url: str\n    is_invisible: bool = False\n    api_domain: Optional[str] = None\n\n\n@enforce_types\n@dataclass\nclass HCaptchaSolution(BaseCaptchaSolution):\n    \"\"\" hCaptcha solution \"\"\"\n\n    token: str\n"
  },
  {
    "path": "unicaps/_captcha/image.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nImage CAPTCHA\n\"\"\"\n\nimport base64\nimport imghdr\nimport io\nimport pathlib\nfrom dataclasses import dataclass\nfrom typing import Union, Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\nfrom ..common import CaptchaAlphabet, CaptchaCharType, WorkerLanguage\nfrom ..exceptions import BadInputDataError\n\n\n@enforce_types\n@dataclass\nclass ImageCaptcha(BaseCaptcha):\n    \"\"\" Image CAPTCHA \"\"\"\n\n    image: Union[bytes, io.RawIOBase, io.BufferedIOBase, pathlib.Path]\n    char_type: Optional[CaptchaCharType] = None\n    is_phrase: Optional[bool] = None\n    is_case_sensitive: Optional[bool] = None\n    is_math: Optional[bool] = None\n    min_len: Optional[int] = None\n    max_len: Optional[int] = None\n    alphabet: Optional[CaptchaAlphabet] = None\n    language: Optional[WorkerLanguage] = None\n    comment: Optional[str] = None\n\n    def __post_init__(self):\n        self._image_bytes = None\n        self.get_image_bytes()\n\n    def get_image_bytes(self) -> bytes:\n        \"\"\" Bytes image \"\"\"\n\n        if self._image_bytes is None:\n            if isinstance(self.image, bytes):\n                self._image_bytes = self.image  # type: ignore\n            elif isinstance(self.image, (io.RawIOBase, io.BufferedIOBase)):\n                self._image_bytes = self.image.read()\n            elif isinstance(self.image, pathlib.Path):\n                self._image_bytes = self.image.read_bytes()\n\n            # check image type\n            self.get_image_type()\n\n        return self._image_bytes\n\n    def get_image_base64(self) -> bytes:\n        \"\"\" BASE64 image \"\"\"\n\n        return base64.b64encode(self.get_image_bytes())\n\n    def get_image_type(self) -> str:\n        \"\"\" Get type of image file/data \"\"\"\n\n        image_type = imghdr.what(None, h=self._image_bytes)\n\n        if not image_type:\n            raise BadInputDataError(\"Unable to recognize image type!\")\n        return image_type\n\n\n@enforce_types\n@dataclass\nclass ImageCaptchaSolution(BaseCaptchaSolution):\n    \"\"\" Image CAPTCHA solution \"\"\"\n\n    text: str\n"
  },
  {
    "path": "unicaps/_captcha/keycaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nKeyCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass KeyCaptcha(BaseCaptcha):\n    \"\"\" KeyCaptcha \"\"\"\n\n    page_url: str\n    user_id: str\n    session_id: str\n    ws_sign: str\n    ws_sign2: str\n\n\n@enforce_types\n@dataclass\nclass KeyCaptchaSolution(BaseCaptchaSolution):\n    \"\"\" KeyCaptcha solution \"\"\"\n\n    token: str\n"
  },
  {
    "path": "unicaps/_captcha/recaptcha_v2.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nGoogle reCAPTCHA v2\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass RecaptchaV2(BaseCaptcha):\n    \"\"\" Google reCAPTCHA v2 \"\"\"\n\n    site_key: str\n    page_url: str\n    is_invisible: bool = False\n    is_enterprise: bool = False\n    data_s: Optional[str] = None\n    api_domain: Optional[str] = None\n\n\n@enforce_types\n@dataclass\nclass RecaptchaV2Solution(BaseCaptchaSolution):\n    \"\"\" Google reCAPTCHA v2 solution \"\"\"\n\n    token: str\n"
  },
  {
    "path": "unicaps/_captcha/recaptcha_v3.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nGoogle reCAPTCHA v3\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass RecaptchaV3(BaseCaptcha):\n    \"\"\" Google reCAPTCHA v3 \"\"\"\n\n    site_key: str\n    page_url: str\n    is_enterprise: bool = False\n    action: Optional[str] = None\n    min_score: Optional[float] = None\n    api_domain: Optional[str] = None\n\n\n@enforce_types\n@dataclass\nclass RecaptchaV3Solution(BaseCaptchaSolution):\n    \"\"\" Google reCAPTCHA v3 solution \"\"\"\n\n    token: str\n"
  },
  {
    "path": "unicaps/_captcha/text.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nText CAPTCHA\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\nfrom ..common import CaptchaAlphabet, WorkerLanguage\n\n\n@enforce_types\n@dataclass\nclass TextCaptcha(BaseCaptcha):\n    \"\"\" Text CAPTCHA \"\"\"\n\n    text: str\n    alphabet: Optional[CaptchaAlphabet] = None\n    language: Optional[WorkerLanguage] = None\n\n\n@enforce_types\n@dataclass\nclass TextCaptchaSolution(BaseCaptchaSolution):\n    \"\"\" Text CAPTCHA solution \"\"\"\n\n    text: str\n"
  },
  {
    "path": "unicaps/_captcha/tiktok.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nTikTokCaptcha\n\"\"\"\n\nfrom dataclasses import dataclass\nfrom typing import Optional\n\nfrom enforce_typing import enforce_types  # type: ignore\n\nfrom .base import BaseCaptcha, BaseCaptchaSolution\n\n\n@enforce_types\n@dataclass\nclass TikTokCaptcha(BaseCaptcha):\n    \"\"\" TikTokCaptcha \"\"\"\n\n    page_url: str\n    aid: Optional[int] = None\n    host: Optional[str] = None\n\n\n@enforce_types\n@dataclass\nclass TikTokCaptchaSolution(BaseCaptchaSolution):\n    \"\"\" TikTokCaptcha solution \"\"\"\n\n    cookies: dict\n"
  },
  {
    "path": "unicaps/_misc/__init__.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nMiscellaneous stuff\n\"\"\"\n"
  },
  {
    "path": "unicaps/_misc/proxy.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nProxy Server representation\n\"\"\"\nimport socket\n\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom typing import Optional\n\n\ndef _is_ip_address(value):\n    try:\n        socket.inet_aton(value)\n        return True\n    except socket.error:\n        return False\n\n\nclass ProxyServerType(Enum):\n    \"\"\" Type of proxy server \"\"\"\n\n    HTTP = 'http'\n    HTTPS = 'https'\n    SOCKS4 = 'socks4'\n    SOCKS5 = 'socks5'\n\n\n@dataclass\nclass ProxyServer:\n    \"\"\" Represents Proxy server \"\"\"\n\n    address: str\n    proxy_type: ProxyServerType = ProxyServerType.HTTP\n    port: int = 80\n    login: Optional[str] = None\n    password: Optional[str] = None\n\n    def __post_init__(self):\n        proxy_string = self.address\n        if '://' in proxy_string:\n            proxy_type, proxy_string = proxy_string.split('://')\n            self.proxy_type = ProxyServerType(proxy_type.lower())\n\n        if '@' in proxy_string:\n            credentials, proxy_string = proxy_string.split('@')\n            self.login, self.password = credentials.split(':', maxsplit=1)\n\n        if ':' in proxy_string:\n            self.address, port = proxy_string.split(':', maxsplit=1)\n            self.port = int(port)\n        else:\n            self.address = proxy_string\n\n    def __str__(self):\n        return self.get_string(including_type=True)\n\n    def get_string(self, including_type=False):\n        \"\"\" Get proxy as string like [<type>://][<login>:<password>@]<addr>:<port> \"\"\"\n\n        proxy_string = ''\n        if including_type:\n            proxy_string += self.proxy_type.value + '://'\n\n        if self.login:\n            proxy_string += self.login + ':' + self.password + '@'\n\n        return proxy_string + self.address + ':' + str(self.port)\n\n    def get_ip_address(self):\n        \"\"\" Get IP address by hostname \"\"\"\n\n        if not _is_ip_address(self.address):\n            return socket.gethostbyname(self.address)\n        return self.address\n"
  },
  {
    "path": "unicaps/_service/__init__.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nCertain services related stuff\n\"\"\"\n\nimport enum\n# pylint: disable=import-self\nfrom . import (\n    anti_captcha, azcaptcha, captcha_guru, cptch_net, deathbycaptcha, rucaptcha, twocaptcha\n)\n\n\nclass CaptchaSolvingService(enum.Enum):\n    \"\"\" CAPTCHA solving service enumeration \"\"\"\n\n    ANTI_CAPTCHA = \"anti-captcha.com\"\n    AZCAPTCHA = \"azcaptcha.com\"\n    CAPTCHA_GURU = \"cap.guru\"\n    CPTCH_NET = \"cptch.net\"\n    DEATHBYCAPTCHA = \"deathbycaptcha.com\"\n    RUCAPTCHA = \"rucaptcha.com\"\n    TWOCAPTCHA = \"2captcha.com\"\n\n\n# supported CAPTCHA solving services\nSOLVING_SERVICE = {\n    CaptchaSolvingService.ANTI_CAPTCHA: anti_captcha,\n    CaptchaSolvingService.AZCAPTCHA: azcaptcha,\n    CaptchaSolvingService.CAPTCHA_GURU: captcha_guru,\n    CaptchaSolvingService.CPTCH_NET: cptch_net,\n    CaptchaSolvingService.DEATHBYCAPTCHA: deathbycaptcha,\n    CaptchaSolvingService.RUCAPTCHA: rucaptcha,\n    CaptchaSolvingService.TWOCAPTCHA: twocaptcha\n}\n"
  },
  {
    "path": "unicaps/_service/anti_captcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nanti-captcha.com service\n\"\"\"\n\nimport json\n\nfrom .base import HTTPService\nfrom .._transport.http_transport import HTTPRequestJSON  # type: ignore\nfrom .. import exceptions\nfrom .._captcha import CaptchaType\nfrom ..common import WorkerLanguage\n\n__all__ = [\n    'Service', 'GetBalanceRequest', 'GetStatusRequest',\n    'ReportGoodRequest', 'ReportBadRequest',\n    'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',\n    'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',\n    'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest',\n    'FunCaptchaTaskRequest', 'FunCaptchaSolutionRequest',\n    'GeeTestTaskRequest', 'GeeTestSolutionRequest',\n    'GeeTestV4TaskRequest', 'GeeTestV4SolutionRequest',\n    'HCaptchaTaskRequest', 'HCaptchaSolutionRequest',\n]\n\n\nclass Service(HTTPService):\n    \"\"\" Main service class for anti-captcha \"\"\"\n\n    BASE_URL = 'https://api.anti-captcha.com'\n\n    def _post_init(self):\n        \"\"\" Init settings \"\"\"\n\n        for captcha_type in self.settings:\n            self.settings[captcha_type].polling_interval = 2\n\n            if captcha_type in (CaptchaType.IMAGE,):\n                self.settings[captcha_type].polling_delay = 5\n                self.settings[captcha_type].solution_timeout = 90\n            else:\n                self.settings[captcha_type].polling_delay = 10\n                self.settings[captcha_type].solution_timeout = 300\n\n\nclass Request(HTTPRequestJSON):\n    \"\"\" Common Request class for anti-captcha \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepares request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(\n                method=\"POST\",\n                json=dict(clientKey=self._service.api_key)\n            )\n        )\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parses response and checks for errors \"\"\"\n\n        response_data = super().parse_response(response)\n\n        error_id = response_data.pop(\"errorId\")\n        if error_id == 0:\n            return response_data\n\n        # ############# #\n        # handle errors #\n        # ############# #\n        error_code = response_data.get(\"errorCode\", f'ERROR {error_id}')\n        error_text = response_data.get(\"errorDescription\", \"\")\n        error_msg = f\"{error_code}: {error_text}\"\n\n        # pylint: disable=no-else-raise\n        if error_code in ('ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST',\n                          'ERROR_IP_NOT_ALLOWED', 'ERROR_IP_BLOCKED'):\n            raise exceptions.AccessDeniedError(error_msg)\n        elif error_code in ('ERROR_ZERO_BALANCE',):\n            raise exceptions.LowBalanceError(error_msg)\n        elif error_code in ('ERROR_NO_SLOT_AVAILABLE',):\n            raise exceptions.ServiceTooBusy(error_msg)\n        elif error_code in ('ERROR_NO_SUCH_METHOD', 'ERROR_NO_SUCH_CAPCHA_ID', 'ERROR_TASK_ABSENT',\n                            'ERROR_TASK_NOT_SUPPORTED', 'ERROR_FUNCAPTCHA_NOT_ALLOWED'):\n            raise exceptions.MalformedRequestError(error_msg)\n        elif error_code in ('ERROR_ZERO_CAPTCHA_FILESIZE', 'ERROR_TOO_BIG_CAPTCHA_FILESIZE',\n                            'ERROR_WRONG_FILE_EXTENSION', 'ERROR_IMAGE_TYPE_NOT_SUPPORTED',\n                            'ERROR_UPLOAD', 'ERROR_PAGEURL', 'ERROR_BAD_TOKEN_OR_PAGEURL',\n                            'ERROR_GOOGLEKEY', 'ERROR_EMPTY_COMMENT',\n                            'ERROR_INCORRECT_SESSION_DATA', 'ERROR_RECAPTCHA_INVALID_SITEKEY',\n                            'ERROR_RECAPTCHA_INVALID_DOMAIN', 'ERROR_RECAPTCHA_OLD_BROWSER',\n                            'ERROR_TOKEN_EXPIRED', 'ERROR_INVISIBLE_RECAPTCHA'):\n            raise exceptions.BadInputDataError(error_msg)\n        elif error_code in ('ERROR_CAPTCHAIMAGE_BLOCKED', 'ERROR_CAPTCHA_UNSOLVABLE',\n                            'ERROR_BAD_DUPLICATES', 'ERROR_RECAPTCHA_TIMEOUT',\n                            'ERROR_FAILED_LOADING_WIDGET'):\n            raise exceptions.UnableToSolveError(error_msg)\n        elif error_code in ('ERROR_PROXY_CONNECT_REFUSED', 'ERROR_PROXY_CONNECT_TIMEOUT',\n                            'ERROR_PROXY_READ_TIMEOUT', 'ERROR_PROXY_BANNED',\n                            'ERROR_PROXY_TRANSPARENT', 'ERROR_PROXY_HAS_NO_IMAGE_SUPPORT',\n                            'ERROR_PROXY_INCOMPATIBLE_HTTP_VERSION', 'ERROR_PROXY_NOT_AUTHORISED'):\n            raise exceptions.ProxyError(error_msg)\n\n        raise exceptions.ServiceError(error_msg)\n\n\nclass GetBalanceRequest(Request):\n    \"\"\" GetBalance Request class \"\"\"\n\n    def prepare(self) -> dict:   # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        request = super().prepare()\n        request.update(dict(url=self._service.BASE_URL + \"/getBalance\"))\n\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parses response and returns task_id \"\"\"\n\n        return dict(balance=float(super().parse_response(response)['balance']))\n\n\nclass GetStatusRequest(GetBalanceRequest):\n    \"\"\" GetStatus Request class \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parses response and returns task_id \"\"\"\n\n        try:\n            return super().parse_response(response)\n        except exceptions.UnicapsException:\n            return {}\n\n\nclass ReportGoodRequest(Request):\n    \"\"\" ReportGood Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        raise exceptions.UnicapsException(\n            \"Report for good CAPTCHA is not supported by the current service!\"\n        )\n\n\nclass ReportBadRequest(Request):\n    \"\"\" ReportBad Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        request = super().prepare(solved_captcha=solved_captcha)\n\n        captcha_type = solved_captcha.task.captcha.get_type()\n\n        if captcha_type == CaptchaType.IMAGE:\n            uri = \"/reportIncorrectImageCaptcha\"\n        elif captcha_type in (CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3):\n            uri = \"/reportIncorrectRecaptcha\"\n        else:\n            raise exceptions.UnicapsException(\n                f\"Report for bad {captcha_type.value} is not supported!\"\n            )\n\n        request.update(dict(url=self._service.BASE_URL + uri))\n        request[\"json\"].update(dict(taskId=int(solved_captcha.captcha_id)))\n        return request\n\n\nclass TaskRequest(Request):\n    \"\"\" Request class for requests to /createTask \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare a request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request.update(dict(url=self._service.BASE_URL + \"/createTask\"))\n        request[\"json\"].update(\n            dict(task={},\n                 softId=940)\n        )\n\n        # add proxy\n        if proxy:\n            request[\"json\"][\"task\"].update(\n                dict(\n                    proxyType=proxy.proxy_type.value,\n                    # Anti-captcha supports IP addresses only\n                    proxyAddress=proxy.get_ip_address(),\n                    proxyPort=proxy.port\n                )\n            )\n\n            if proxy.login:\n                request[\"json\"][\"task\"].update(\n                    dict(\n                        proxyLogin=proxy.login,\n                        proxyPassword=proxy.password\n                    )\n                )\n\n        if user_agent:\n            request[\"json\"][\"task\"][\"userAgent\"] = user_agent\n\n        if cookies:\n            request[\"json\"][\"task\"][\"cookies\"] = '; '.join(f'{k}={v}'for k, v in cookies.items())\n\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parses response and returns task_id \"\"\"\n\n        response_data = super().parse_response(response)\n\n        return {\"task_id\": response_data.pop(\"taskId\"),\n                \"extra\": response_data}\n\n\nclass SolutionRequest(Request):\n    \"\"\" Request class for requests to /getTaskResult \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, task) -> dict:  # type: ignore\n        \"\"\" Prepare a request \"\"\"\n\n        request = super().prepare(task=task)\n        request.update(dict(url=self._service.BASE_URL + \"/getTaskResult\"))\n        request[\"json\"].update(dict(taskId=str(task.task_id)))\n\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parses response and returns solution and cost \"\"\"\n\n        response_data = super().parse_response(response)\n\n        if response_data[\"status\"] != \"ready\":\n            raise exceptions.SolutionNotReadyYet()\n\n        solution_data = response_data[\"solution\"]\n        solution_class = self.source_data['task'].captcha.get_solution_class()\n        captcha_type = self.source_data['task'].captcha.get_type()\n        args = []\n        kwargs = {}\n        if captcha_type in (CaptchaType.IMAGE,):\n            args.append(solution_data.pop('text'))\n        elif captcha_type in (CaptchaType.RECAPTCHAV2, CaptchaType.RECAPTCHAV3,\n                              CaptchaType.HCAPTCHA):\n            args.append(solution_data.pop('gRecaptchaResponse'))\n        elif captcha_type in (CaptchaType.FUNCAPTCHA,):\n            args.append(solution_data.pop('token'))\n        elif captcha_type in (CaptchaType.GEETEST, CaptchaType.GEETESTV4):\n            kwargs.update(solution_data)\n        else:\n            kwargs.update(solution_data)\n\n        solution = solution_class(*args, **kwargs)\n\n        if \"cost\" in response_data:\n            cost = response_data.pop(\"cost\")\n        else:\n            cost = None\n\n        return dict(\n            solution=solution,\n            cost=cost,\n            extra=response_data\n        )\n\n\nclass ImageCaptchaTaskRequest(TaskRequest):\n    \"\"\" ImageCaptchaTask Request class \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare a request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=None,\n            user_agent=None,\n            cookies=None\n        )\n\n        task_data = dict(\n            type=\"ImageToTextTask\",\n            body=captcha.get_image_base64().decode('ascii')\n        )\n        task_data.update(\n            captcha.get_optional_data(\n                is_case_sensitive=('case', None),\n                is_phrase=('phrase', None),\n                is_math=('math', None),\n                char_type=('numeric', lambda v: v.value if v.value in (1, 2) else None),\n                min_len=('minLength', None),\n                max_len=('maxLength', None),\n                comment=('comment', None)\n            )\n        )\n        request['json']['task'].update(task_data)\n\n        # set workers pool language\n        if captcha.language:\n            request['json']['languagePool'] = (\n                'rn' if captcha.language == WorkerLanguage.RUSSIAN else 'en'\n            )\n\n        return request\n\n\nclass ImageCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" Image CAPTCHA solution request \"\"\"\n\n\nclass RecaptchaV2TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v2 task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        if proxy:\n            kwargs = dict(captcha=captcha, proxy=proxy, user_agent=user_agent, cookies=cookies)\n            task_type = \"RecaptchaV2EnterpriseTask\" if captcha.is_enterprise else \"NoCaptchaTask\"\n        else:\n            kwargs = dict(captcha=captcha, proxy=None, user_agent=None, cookies=None)\n            task_type = (\"RecaptchaV2EnterpriseTaskProxyless\" if captcha.is_enterprise\n                         else \"NoCaptchaTaskProxyless\")\n\n        request = super().prepare(**kwargs)\n        request['json']['task'].update(\n            dict(\n                type=task_type,\n                websiteURL=captcha.page_url,\n                websiteKey=captcha.site_key,\n                isInvisible=captcha.is_invisible\n            )\n        )\n\n        # if enterprise captcha\n        if captcha.is_enterprise:\n            request['json']['task']['enterprisePayload'] = dict(s=captcha.data_s)\n        else:\n            # set optional data if any\n            request['json']['task'].update(\n                captcha.get_optional_data(\n                    data_s=('recaptchaDataSValue', None)\n                )\n            )\n\n        # set optional api_domain if any\n        request['json']['task'].update(\n            captcha.get_optional_data(\n                api_domain=('apiDomain', None)\n            )\n        )\n\n        return request\n\n\nclass RecaptchaV2SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v2 solution request \"\"\"\n\n\nclass RecaptchaV3TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v3 task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=None,\n            user_agent=None,\n            cookies=None\n        )\n\n        request['json']['task'].update(\n            dict(\n                type=\"RecaptchaV3TaskProxyless\",\n                websiteURL=captcha.page_url,\n                websiteKey=captcha.site_key\n            )\n        )\n        # set optional data if any\n        request['json']['task'].update(\n            captcha.get_optional_data(\n                min_score=('minScore', None),\n                action=('pageAction', None),\n                api_domain=('apiDomain', None)\n            )\n        )\n\n        # if enterprise captcha\n        if captcha.is_enterprise:\n            request['json']['task']['isEnterprise'] = True\n\n        return request\n\n\nclass RecaptchaV3SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v3 solution request \"\"\"\n\n\nclass FunCaptchaTaskRequest(TaskRequest):\n    \"\"\" FunCaptcha task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        if proxy:\n            kwargs = dict(captcha=captcha, proxy=proxy, user_agent=user_agent, cookies=cookies)\n            task_type = \"FunCaptchaTask\"\n        else:\n            kwargs = dict(captcha=captcha, proxy=None, user_agent=None, cookies=None)\n            task_type = \"FunCaptchaTaskProxyless\"\n\n        request = super().prepare(**kwargs)\n        request['json']['task'].update(\n            dict(\n                type=task_type,\n                websiteURL=captcha.page_url,\n                websitePublicKey=captcha.public_key\n            )\n        )\n        # set optional data if any\n        request['json']['task'].update(\n            captcha.get_optional_data(\n                service_url=('funcaptchaApiJSSubdomain', None),\n            )\n        )\n\n        # add blob value\n        if captcha.blob:\n            request['json']['task']['data'] = json.dumps(dict(blob=captcha.blob))\n\n        return request\n\n\nclass FunCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" FunCaptcha solution request \"\"\"\n\n\nclass GeeTestTaskRequest(TaskRequest):\n    \"\"\" GeeTest task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        if proxy:\n            kwargs = dict(captcha=captcha, proxy=proxy, user_agent=user_agent, cookies=cookies)\n            task_type = \"GeeTestTask\"\n        else:\n            kwargs = dict(captcha=captcha, proxy=None, user_agent=None, cookies=None)\n            task_type = \"GeeTestTaskProxyless\"\n\n        request = super().prepare(**kwargs)\n        request['json']['task'].update(\n            dict(\n                type=task_type,\n                websiteURL=captcha.page_url,\n                gt=captcha.gt_key,\n                challenge=captcha.challenge\n            )\n        )\n\n        # set optional data if any\n        request['json']['task'].update(\n            captcha.get_optional_data(\n                api_server=('geetestApiServerSubdomain', None),\n            )\n        )\n\n        return request\n\n\nclass GeeTestSolutionRequest(SolutionRequest):\n    \"\"\" GeeTest solution request \"\"\"\n\n\nclass GeeTestV4TaskRequest(TaskRequest):\n    \"\"\" GeeTest task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        if proxy:\n            kwargs = dict(captcha=captcha, proxy=proxy, user_agent=user_agent, cookies=cookies)\n            task_type = \"GeeTestTask\"\n        else:\n            kwargs = dict(captcha=captcha, proxy=None, user_agent=None, cookies=None)\n            task_type = \"GeeTestTaskProxyless\"\n\n        request = super().prepare(**kwargs)\n        request['json']['task'].update(\n            dict(\n                type=task_type,\n                websiteURL=captcha.page_url,\n                gt=captcha.captcha_id,\n                version=4\n            )\n        )\n\n        return request\n\n\nclass GeeTestV4SolutionRequest(SolutionRequest):\n    \"\"\" GeeTest solution request \"\"\"\n\n\nclass HCaptchaTaskRequest(TaskRequest):\n    \"\"\" hCaptcha task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n\n        if proxy:\n            kwargs = dict(captcha=captcha, proxy=proxy, user_agent=user_agent, cookies=cookies)\n            task_type = \"HCaptchaTask\"\n        else:\n            kwargs = dict(captcha=captcha, proxy=None, user_agent=user_agent, cookies=None)\n            task_type = \"HCaptchaTaskProxyless\"\n\n        request = super().prepare(**kwargs)\n        request['json']['task'].update(\n            dict(\n                type=task_type,\n                websiteURL=captcha.page_url,\n                websiteKey=captcha.site_key,\n                isInvisible=captcha.is_invisible\n            )\n        )\n\n        return request\n\n\nclass HCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" hCaptcha solution request \"\"\"\n"
  },
  {
    "path": "unicaps/_service/azcaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nazcaptcha.com service\n\"\"\"\nfrom .base import HTTPService\nfrom .._transport.http_transport import HTTPRequestJSON  # type: ignore\nfrom .. import exceptions\nfrom .._captcha import CaptchaType\nfrom ..common import CaptchaAlphabet\n\n__all__ = [\n    'Service', 'GetBalanceRequest', 'GetStatusRequest',\n    'ReportGoodRequest', 'ReportBadRequest',\n    'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',\n    'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',\n    'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest',\n    'HCaptchaTaskRequest', 'HCaptchaSolutionRequest',\n    'FunCaptchaTaskRequest', 'FunCaptchaSolutionRequest'\n]\n\n\nclass Service(HTTPService):\n    \"\"\" Main service class for 2captcha \"\"\"\n\n    BASE_URL = 'http://azcaptcha.com'\n\n    def _post_init(self):\n        \"\"\" Init settings \"\"\"\n\n        for captcha_type in self.settings:\n            self.settings[captcha_type].polling_interval = 5\n            self.settings[captcha_type].solution_timeout = 180\n            if captcha_type in (CaptchaType.RECAPTCHAV2, CaptchaType.HCAPTCHA):\n                self.settings[captcha_type].polling_delay = 20\n                self.settings[captcha_type].solution_timeout = 300\n            elif captcha_type in (CaptchaType.RECAPTCHAV3,):\n                self.settings[captcha_type].polling_delay = 15\n            else:\n                self.settings[captcha_type].polling_delay = 5\n\n\nclass Request(HTTPRequestJSON):\n    \"\"\" Common Request class for 2captcha \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and checks for errors \"\"\"\n\n        response_data = super().parse_response(response)\n\n        if response_data.pop(\"status\") == 1:\n            return response_data\n\n        ###############\n        # handle errors\n        ###############\n        error_code = response_data[\"request\"]\n        error_text = response_data.get(\"error_text\", \"\")\n        error_msg = f\"{error_code}: {error_text}\"\n\n        if error_code == 'CAPCHA_NOT_READY':  # pylint: disable=no-else-raise\n            raise exceptions.SolutionNotReadyYet()\n        elif error_code in ('ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST',\n                            'ERROR_IP_NOT_ALLOWED', 'IP_BANNED'):\n            raise exceptions.AccessDeniedError(error_msg)\n        elif error_code in ('ERROR_ZERO_BALANCE',):\n            raise exceptions.LowBalanceError(error_msg)\n        elif error_code in ('ERROR_NO_SLOT_AVAILABLE',):\n            # If server returns ERROR_NO_SLOT_AVAILABLE make a 5 seconds timeout before sending\n            # next request.\n            # time.sleep(5)\n            raise exceptions.ServiceTooBusy(error_msg)\n        elif error_code in ('MAX_USER_TURN',) or error_code.startswith('ERROR:'):\n            raise exceptions.TooManyRequestsError(error_msg)\n        elif error_code in ('ERROR_WRONG_ID_FORMAT', 'ERROR_WRONG_CAPTCHA_ID'):\n            raise exceptions.MalformedRequestError(error_msg)\n        elif error_code in ('ERROR_ZERO_CAPTCHA_FILESIZE', 'ERROR_TOO_BIG_CAPTCHA_FILESIZE',\n                            'ERROR_WRONG_FILE_EXTENSION', 'ERROR_IMAGE_TYPE_NOT_SUPPORTED',\n                            'ERROR_UPLOAD', 'ERROR_PAGEURL', 'ERROR_BAD_TOKEN_OR_PAGEURL',\n                            'ERROR_GOOGLEKEY', 'ERROR_BAD_PARAMETERS', 'ERROR_TOKEN_EXPIRED',\n                            'ERROR_EMPTY_ACTION'):\n            raise exceptions.BadInputDataError(error_msg)\n        elif error_code in ('ERROR_CAPTCHAIMAGE_BLOCKED', 'ERROR_CAPTCHA_UNSOLVABLE',\n                            'ERROR_BAD_DUPLICATES'):\n            raise exceptions.UnableToSolveError(error_msg)\n        elif error_code in ('ERROR_BAD_PROXY', 'ERROR_PROXY_CONNECTION_FAILED'):\n            raise exceptions.ProxyError(error_msg)\n\n        raise exceptions.ServiceError(error_msg)\n\n\nclass InRequest(Request):\n    \"\"\" Request class for requests to /in.php \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(\n                method=\"POST\",\n                url=self._service.BASE_URL + \"/in.php\",\n                data=dict(\n                    key=self._service.api_key,\n                    json=1,\n                    # soft_id=2738\n                )\n            )\n        )\n\n        # azcaptcha.com doesn't like headers - returns ERROR_UPLOAD\n        if 'headers' in request:\n            del request['headers']\n\n        return request\n\n\nclass ResRequest(Request):\n    \"\"\" Request class for requests to /res.php \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(\n                method=\"GET\",\n                url=self._service.BASE_URL + \"/res.php\",\n                params=dict(\n                    key=self._service.api_key,\n                    json=1\n                )\n            )\n        )\n\n        # azcaptcha.com doesn't like headers - returns ERROR_UPLOAD\n        if 'headers' in request:\n            del request['headers']\n\n        return request\n\n\nclass GetBalanceRequest(ResRequest):\n    \"\"\" GetBalance Request class \"\"\"\n\n    def prepare(self) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare()\n        request[\"params\"].update(dict(action=\"getbalance\"))\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return balance \"\"\"\n\n        return {'balance': float(super().parse_response(response)[\"request\"])}\n\n\nclass GetStatusRequest(GetBalanceRequest):\n    \"\"\" GetStatus Request class \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return status \"\"\"\n\n        try:\n            return super().parse_response(response)\n        except exceptions.UnicapsException:\n            return {}\n\n\nclass ReportGoodRequest(ResRequest):\n    \"\"\" ReportGood Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(solved_captcha=solved_captcha)\n        request[\"params\"].update(\n            dict(\n                action=\"reportgood\",\n                id=solved_captcha.captcha_id\n            )\n        )\n        return request\n\n\nclass ReportBadRequest(ResRequest):\n    \"\"\" ReportBad Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(solved_captcha=solved_captcha)\n        request[\"params\"].update(\n            dict(\n                action=\"reportbad\",\n                id=solved_captcha.captcha_id\n            )\n        )\n        return request\n\n\nclass TaskRequest(InRequest):\n    \"\"\" Common Task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument\n    def prepare(self, captcha, proxy, user_agent, cookies):\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        if proxy:\n            request['data'].update(\n                dict(\n                    proxy=proxy.get_string(),\n                    proxytype=proxy.proxy_type.value.upper()\n                )\n            )\n\n        if cookies:\n            request['data']['cookies'] = ';'.join([f'{k}:{v}' for k, v in cookies.items()])\n\n        if user_agent:\n            request['data']['userAgent'] = user_agent\n\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return task_id \"\"\"\n\n        response_data = super().parse_response(response)\n\n        return dict(\n            task_id=response_data.pop(\"request\"),\n            extra=response_data\n        )\n\n\nclass SolutionRequest(ResRequest):\n    \"\"\" Common Solution Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, task) -> dict:  # type: ignore\n        \"\"\" Prepare a request \"\"\"\n\n        request = super().prepare(task=task)\n        request[\"params\"].update(\n            dict(action=\"get\", id=task.task_id)\n        )\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return solution and cost \"\"\"\n\n        response_data = super().parse_response(response)\n\n        # get solution class\n        solution_class = self.source_data['task'].captcha.get_solution_class()\n\n        return dict(\n            solution=solution_class(response_data.pop(\"request\")),\n            cost=None,\n            extra=response_data\n        )\n\n\nclass ImageCaptchaTaskRequest(TaskRequest):\n    \"\"\" ImageCaptchaTask Request class \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=None,\n            user_agent=None,\n            cookies=None\n        )\n\n        # add required params\n        request['data'].update(\n            dict(\n                method=\"base64\",\n                body=captcha.get_image_base64().decode('ascii')\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                is_phrase=('phrase', lambda v: int(bool(v))),\n                is_case_sensitive=('regsense', lambda v: int(bool(v))),\n                char_type=('numeric', lambda v: v.value),\n                is_math=('calc', lambda v: int(bool(v))),\n                min_len=('min_len', None),\n                max_len=('max_len', None),\n                alphabet=('language',\n                          lambda v: {CaptchaAlphabet.CYRILLIC: 1,\n                                     CaptchaAlphabet.LATIN: 2}.get(v, 0)),\n                language=('lang', lambda v: v.value),\n                comment=('textinstructions', None),\n            )\n        )\n\n        return request\n\n\nclass ImageCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" Image CAPTCHA solution request \"\"\"\n\n\nclass RecaptchaV2TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v2 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"userrecaptcha\",\n                googlekey=captcha.site_key,\n                pageurl=captcha.page_url,\n                invisible=int(captcha.is_invisible)\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                data_s=('data-s', None)\n            )\n        )\n\n        return request\n\n\nclass RecaptchaV2SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v2 solution request \"\"\"\n\n\nclass RecaptchaV3TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v3 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"userrecaptcha\",\n                version=\"v3\",\n                googlekey=captcha.site_key,\n                pageurl=captcha.page_url,\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                action=('action', None),\n                min_score=('min_score', None)\n            )\n        )\n\n        return request\n\n\nclass RecaptchaV3SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v3 solution request \"\"\"\n\n\nclass HCaptchaTaskRequest(TaskRequest):\n    \"\"\" HCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"hcaptcha\",\n                sitekey=captcha.site_key,\n                pageurl=captcha.page_url\n            )\n        )\n\n        return request\n\n\nclass HCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" HCaptcha solution request \"\"\"\n\n\nclass FunCaptchaTaskRequest(TaskRequest):\n    \"\"\" FunCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"funcaptcha\",\n                publickey=captcha.public_key,\n                pageurl=captcha.page_url\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                service_url=('surl', None),\n                blob=('data[blob]', None),\n            )\n        )\n\n        return request\n\n\nclass FunCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" FunCaptcha solution request \"\"\"\n"
  },
  {
    "path": "unicaps/_service/base.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nBase service stuff\n\"\"\"\n\nimport asyncio\nimport time\nfrom abc import ABC, abstractmethod\nfrom dataclasses import dataclass\nfrom datetime import datetime\nfrom inspect import getmodule\nfrom timeit import default_timer as timer\nfrom typing import Dict, Optional, Tuple\n\nfrom .._transport.http_transport import StandardHTTPTransport  # type: ignore\nfrom .._captcha import CaptchaType\nfrom .._captcha.base import BaseCaptcha, BaseCaptchaSolution\nfrom .._misc.proxy import ProxyServer\nfrom ..exceptions import UnicapsException, SolutionWaitTimeout, SolutionNotReadyYet\n\n\nclass BaseService(ABC):\n    \"\"\" Base class for all services \"\"\"\n\n    def __init__(self, api_key: str):\n        self.api_key = api_key\n        self._transport = self._init_transport()\n        self._module = getmodule(self)\n        self._settings = {captcha_type: Settings() for captcha_type in self.supported_captchas}\n        self._post_init()\n\n    @abstractmethod\n    def _init_transport(self):\n        pass\n\n    def _post_init(self):\n        pass\n\n    def _make_request(self, request_class, *args):\n        request_class = request_class + \"Request\"\n        if not hasattr(self._module, request_class):\n            raise UnicapsException(f\"{request_class} is not supported by the current service!\")\n\n        request = getattr(self._module, request_class)(self)\n        return self._transport.make_request(request, *args)\n\n    async def _make_request_async(self, request_class, *args):\n        request_class = request_class + \"Request\"\n        if not hasattr(self._module, request_class):\n            raise UnicapsException(f\"{request_class} is not supported by the current service!\")\n\n        request = getattr(self._module, request_class)(self)\n        return await self._transport.make_request_async(request, *args)\n\n    @property\n    def supported_captchas(self) -> Tuple[CaptchaType, ...]:\n        \"\"\" List of supported captchas \"\"\"\n\n        captchas = []\n        for captcha_type in CaptchaType:\n            if hasattr(self._module, captcha_type.value + \"TaskRequest\"):\n                captchas.append(captcha_type)\n        return tuple(captchas)\n\n    @property\n    def settings(self) -> Dict[CaptchaType, 'Settings']:\n        \"\"\" Service settings \"\"\"\n\n        return self._settings\n\n    def solve_captcha(self, captcha: BaseCaptcha, proxy: Optional[ProxyServer] = None,\n                      user_agent: Optional[str] = None,\n                      cookies: Optional[Dict[str, str]] = None) -> 'SolvedCaptcha':\n        \"\"\" Solves captcha and returns SolvedCaptcha object \"\"\"\n\n        start_time = datetime.now()\n        task = self.create_task(captcha, proxy, user_agent, cookies)\n        solution, cost, extra = self.wait_for_solution(task)\n        end_time = datetime.now()\n\n        return SolvedCaptcha(task, solution, start_time, end_time,\n                             cost=cost, extra=extra)\n\n    async def solve_captcha_async(self, captcha: BaseCaptcha, proxy: Optional[ProxyServer] = None,\n                                  user_agent: Optional[str] = None,\n                                  cookies: Optional[Dict[str, str]] = None) -> 'AsyncSolvedCaptcha':\n        \"\"\" Solves captcha and returns SolvedCaptcha object (async) \"\"\"\n\n        start_time = datetime.now()\n        task = await self.create_task_async(captcha, proxy, user_agent, cookies)\n        solution, cost, extra = await self.wait_for_solution_async(task)\n        end_time = datetime.now()\n\n        return AsyncSolvedCaptcha(task, solution, start_time, end_time,\n                                  cost=cost, extra=extra)\n\n    def create_task(self, captcha: BaseCaptcha, proxy: Optional[ProxyServer] = None,\n                    user_agent: Optional[str] = None,\n                    cookies: Optional[Dict[str, str]] = None) -> 'CaptchaTask':\n        \"\"\" Creates task for solving a CAPTCHA \"\"\"\n\n        captcha_type = captcha.get_type()\n\n        if captcha_type not in self.supported_captchas:\n            raise UnicapsException(f\"{captcha_type} is not supported by the current service!\")\n\n        result = self._make_request(\n            f\"{captcha_type.value}Task\", captcha, proxy, user_agent, cookies\n        )\n        task_id = str(result[\"task_id\"])\n\n        return CaptchaTask(self, captcha, task_id, result.get(\"extra\"))\n\n    async def create_task_async(self, captcha: BaseCaptcha, proxy: Optional[ProxyServer] = None,\n                                user_agent: Optional[str] = None,\n                                cookies: Optional[Dict[str, str]] = None) -> 'AsyncCaptchaTask':\n        \"\"\" Creates CAPTCHA solving task (async) \"\"\"\n\n        captcha_type = captcha.get_type()\n\n        if captcha_type not in self.supported_captchas:\n            raise UnicapsException(f\"{captcha_type} is not supported by the current service!\")\n\n        result = await self._make_request_async(\n            f\"{captcha_type.value}Task\", captcha, proxy, user_agent, cookies\n        )\n        task_id = str(result[\"task_id\"])\n\n        return AsyncCaptchaTask(self, captcha, task_id, result.get(\"extra\"))\n\n    def get_task_result(self, task: 'CaptchaTask') -> Tuple[BaseCaptchaSolution,\n                                                            Optional[float], Dict]:\n        \"\"\" Returns CAPTCHA solution \"\"\"\n\n        result = self._make_request(f\"{task.captcha.get_type().value}Solution\", task)\n\n        return (\n            result['solution'],  # type: ignore\n            float(result['cost']) if result.get('cost') else None,\n            result.get(\"extra\") or {}\n        )\n\n    async def get_task_result_async(self, task: 'CaptchaTask') -> Tuple[BaseCaptchaSolution,\n                                                                        Optional[float], Dict]:\n        \"\"\" Returns CAPTCHA solution \"\"\"\n\n        result = await self._make_request_async(f\"{task.captcha.get_type().value}Solution\", task)\n\n        return (\n            result['solution'],  # type: ignore\n            float(result['cost']) if result.get('cost') else None,\n            result.get(\"extra\") or {}\n        )\n\n    def wait_for_solution(self, task) -> Tuple[BaseCaptchaSolution, Optional[float], Dict]:\n        \"\"\" Wait for CAPTCHA solution \"\"\"\n\n        settings = self._settings[task.captcha.get_type()]\n\n        start_time = timer()\n        time.sleep(settings.polling_delay)\n        while True:\n            if timer() - start_time > settings.solution_timeout:\n                raise SolutionWaitTimeout(\n                    f\"Couldn't receive a solution in {settings.solution_timeout} seconds!\"\n                )\n\n            try:\n                return task.get_result()\n            except SolutionNotReadyYet:\n                time.sleep(settings.polling_interval)\n\n    async def wait_for_solution_async(self, task) -> Tuple[BaseCaptchaSolution,\n                                                           Optional[float], Dict]:\n        \"\"\" Wait for CAPTCHA solution \"\"\"\n\n        settings = self._settings[task.captcha.get_type()]\n\n        start_time = timer()\n        await asyncio.sleep(settings.polling_delay)\n        while True:\n            if timer() - start_time > settings.solution_timeout:\n                raise SolutionWaitTimeout(\n                    f\"Couldn't receive a solution in {settings.solution_timeout} seconds!\"\n                )\n\n            try:\n                return await task.get_result()\n            except SolutionNotReadyYet:\n                await asyncio.sleep(settings.polling_interval)\n\n    def get_balance(self):\n        \"\"\" Get account balance \"\"\"\n\n        balance = self._make_request(\"GetBalance\").get('balance')\n        if balance is not None:\n            balance = float(balance)\n        return balance\n\n    async def get_balance_async(self):\n        \"\"\" Get account balance \"\"\"\n\n        response = await self._make_request_async(\"GetBalance\")\n        balance = response.get('balance')\n        if balance is not None:\n            balance = float(balance)\n        return balance\n\n    def get_status(self) -> bool:\n        \"\"\" Get service status \"\"\"\n\n        return bool(self._make_request(\"GetStatus\"))\n\n    async def get_status_async(self) -> bool:\n        \"\"\" Get service status \"\"\"\n\n        return bool(await self._make_request_async(\"GetStatus\"))\n\n    def report_good(self, solved_captcha: 'SolvedCaptcha', raise_exc: bool = False) -> bool:\n        \"\"\" Report good CAPTCHA \"\"\"\n\n        result = False\n        try:\n            result = self._make_request(\"ReportGood\", solved_captcha)\n        except UnicapsException:\n            if raise_exc:\n                raise\n        return bool(result)\n\n    async def report_good_async(self, solved_captcha: 'SolvedCaptcha',\n                                raise_exc: bool = False) -> bool:\n        \"\"\" Report good CAPTCHA \"\"\"\n\n        result = False\n        try:\n            result = await self._make_request_async(\"ReportGood\", solved_captcha)\n        except UnicapsException:\n            if raise_exc:\n                raise\n        return bool(result)\n\n    def report_bad(self, solved_captcha: 'SolvedCaptcha', raise_exc: bool = False) -> bool:\n        \"\"\" Report bad CAPTCHA \"\"\"\n\n        result = False\n        try:\n            result = self._make_request(\"ReportBad\", solved_captcha)\n        except UnicapsException:\n            if raise_exc:\n                raise\n        return bool(result)\n\n    async def report_bad_async(self, solved_captcha: 'SolvedCaptcha',\n                               raise_exc: bool = False) -> bool:\n        \"\"\" Report bad CAPTCHA \"\"\"\n\n        result = False\n        try:\n            result = await self._make_request_async(\"ReportBad\", solved_captcha)\n        except UnicapsException:\n            if raise_exc:\n                raise\n        return bool(result)\n\n    @abstractmethod\n    def close(self):\n        \"\"\" Close connections \"\"\"\n\n    @abstractmethod\n    async def close_async(self):\n        \"\"\" Close connections (async) \"\"\"\n\n\nclass HTTPService(BaseService):\n    \"\"\" Standard HTTP Service \"\"\"\n\n    def _init_transport(self):\n        return StandardHTTPTransport()\n\n    def close(self):\n        \"\"\" Close connections \"\"\"\n        self._transport.close()\n\n    async def close_async(self):\n        \"\"\" Close connections (async) \"\"\"\n        await self._transport.close_async()\n\n\n@dataclass\nclass Settings:\n    \"\"\" Service settings \"\"\"\n\n    polling_delay: int = 5  # seconds before starting to check for sollution\n    polling_interval: int = 2  # seconds between checks\n    solution_timeout: int = 300  # seconds is solution timeout\n\n\nclass CaptchaTask:\n    \"\"\" Task for CAPTCHA solving \"\"\"\n\n    def __init__(self, service, captcha: BaseCaptcha, task_id: str, extra: Dict = None):\n        self._service = service\n        self._captcha = captcha\n        self._task_id = task_id\n        self._extra = extra or {}\n        self._result = None\n\n    @property\n    def task_id(self) -> str:\n        \"\"\" Task ID \"\"\"\n        return self._task_id\n\n    @property\n    def captcha(self) -> BaseCaptcha:\n        \"\"\" Source CAPTCHA \"\"\"\n        return self._captcha\n\n    @property\n    def extra(self) -> Dict:\n        \"\"\" Task extra data \"\"\"\n        return self._extra\n\n    def get_result(self) -> Optional[BaseCaptchaSolution]:\n        \"\"\" Gets solution \"\"\"\n        if self._result is None:\n            self._result = self._service.get_task_result(self)\n        return self._result\n\n    def is_done(self) -> bool:\n        \"\"\" Checks if solution is ready \"\"\"\n        return bool(self._result)\n\n    def wait(self) -> BaseCaptchaSolution:\n        \"\"\" Waits for solution \"\"\"\n        return self._service.wait_for_solution(self)\n\n\nclass AsyncCaptchaTask(CaptchaTask):\n    \"\"\" Task for CAPTCHA solving \"\"\"\n\n    async def get_result(self) -> Optional[BaseCaptchaSolution]:  # type: ignore\n        \"\"\" Gets solution \"\"\"\n        if self._result is None:\n            self._result = await self._service.get_task_result_async(self)\n        return self._result\n\n    async def wait(self) -> BaseCaptchaSolution:  # type: ignore\n        \"\"\" Waits for solution \"\"\"\n        return await self._service.wait_for_solution_async(self)\n\n\nclass SolvedCaptcha:\n    \"\"\" Solved CAPTCHA object \"\"\"\n\n    def __init__(self, task: CaptchaTask, solution: BaseCaptchaSolution, start_time: datetime,\n                 end_time: datetime, cost: Optional[float] = None, cookies: Optional[dict] = None,\n                 extra: dict = None):\n        if not task.is_done():\n            raise UnicapsException(\"CAPTCHA is not solved yet!\")\n\n        self._task = task\n        self._solution = solution\n        self._start_time = start_time\n        self._end_time = end_time\n        self._cost = cost\n        self._cookies = cookies or {}\n        self._extra = extra or {}\n\n    @property\n    def captcha_id(self) -> str:\n        \"\"\" CAPTCHA ID (usually it's the same as task ID) \"\"\"\n        return self._task.task_id\n\n    @property\n    def task(self) -> CaptchaTask:\n        \"\"\" Task for solving \"\"\"\n        return self._task\n\n    @property\n    def solution(self) -> BaseCaptchaSolution:\n        \"\"\" CAPTCHA solution \"\"\"\n        return self._solution\n\n    @property\n    def start_time(self) -> datetime:\n        \"\"\" Start solving at \"\"\"\n        return self._start_time\n\n    @property\n    def end_time(self) -> datetime:\n        \"\"\" End solving at \"\"\"\n        return self._end_time\n\n    @property\n    def cost(self) -> Optional[float]:\n        \"\"\" The cost of solved CAPTCHA \"\"\"\n        return self._cost\n\n    @property\n    def cookies(self) -> dict:\n        \"\"\" Cookies \"\"\"\n        return self._cookies\n\n    @property\n    def extra(self) -> dict:\n        \"\"\" Extra data from the service \"\"\"\n        return self._extra\n\n    def report_good(self, raise_exc: bool = False) -> bool:\n        \"\"\" Report good CAPTCHA \"\"\"\n        # pylint: disable=protected-access\n        return self._task._service.report_good(self, raise_exc=raise_exc)\n\n    def report_bad(self, raise_exc: bool = False) -> bool:\n        \"\"\" Report bad CAPTCHA \"\"\"\n        # pylint: disable=protected-access\n        return self._task._service.report_bad(self, raise_exc=raise_exc)\n\n\nclass AsyncSolvedCaptcha(SolvedCaptcha):\n    \"\"\" Solved CAPTCHA object (async) \"\"\"\n\n    async def report_good(self, raise_exc: bool = False) -> bool:  # type: ignore\n        \"\"\" Report good CAPTCHA \"\"\"\n        # pylint: disable=protected-access\n        return await self._task._service.report_good_async(self, raise_exc=raise_exc)\n\n    async def report_bad(self, raise_exc: bool = False) -> bool:  # type: ignore\n        \"\"\" Report bad CAPTCHA \"\"\"\n        # pylint: disable=protected-access\n        return await self._task._service.report_bad_async(self, raise_exc=raise_exc)\n"
  },
  {
    "path": "unicaps/_service/captcha_guru.py",
    "content": "\"\"\"\ncap.guru service\n\"\"\"\n\n# pylint: disable=unused-import\nfrom .twocaptcha import (\n    Service as Service2Captcha, GetBalanceRequest, GetStatusRequest,\n    ReportGoodRequest, ReportBadRequest,\n    ImageCaptchaTaskRequest, ImageCaptchaSolutionRequest,\n    RecaptchaV2TaskRequest, RecaptchaV2SolutionRequest,\n    RecaptchaV3TaskRequest, RecaptchaV3SolutionRequest,\n    HCaptchaTaskRequest, HCaptchaSolutionRequest,\n    GeeTestTaskRequest, GeeTestSolutionRequest\n)\n\n__all__ = [\n    'Service', 'GetBalanceRequest', 'GetStatusRequest',\n    'ReportGoodRequest', 'ReportBadRequest',\n    'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',\n    'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',\n    'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest',\n    'HCaptchaTaskRequest', 'HCaptchaSolutionRequest',\n    'GeeTestTaskRequest', 'GeeTestSolutionRequest'\n]\n\n\nclass Service(Service2Captcha):\n    \"\"\" Main service class for cap.guru \"\"\"\n\n    BASE_URL = 'http://api.cap.guru'\n\n\ndef _decorator(cls):\n    \"\"\" Decorator for *TaskRequest class \"\"\"\n\n    # pylint: disable=missing-function-docstring\n    class Wrapper:\n        \"\"\" A wrapper for *TaskRequest class \"\"\"\n        def __init__(self, *args, **kwargs):\n            self.decorated_obj = cls(*args, **kwargs)\n\n        def prepare(self, *args, **kwargs):\n            result = self.decorated_obj.prepare(*args, **kwargs)\n            if 'data' in result:\n                result['params'] = result.pop('data')\n                result['method'] = 'GET'\n\n                if 'soft_id' in result['params']:\n                    del result['params']['soft_id']\n                    result['params']['softguru'] = '127872'\n\n            return result\n\n        def parse_response(self, *args, **kwargs):\n            return self.decorated_obj.parse_response(*args, **kwargs)\n\n        def process_response(self, *args, **kwargs):\n            return self.decorated_obj.process_response(*args, **kwargs)\n\n    return Wrapper\n\n\nImageCaptchaTaskRequest = _decorator(ImageCaptchaTaskRequest)  # type: ignore\nRecaptchaV2TaskRequest = _decorator(RecaptchaV2TaskRequest)  # type: ignore\nRecaptchaV3TaskRequest = _decorator(RecaptchaV3TaskRequest)  # type: ignore\nHCaptchaTaskRequest = _decorator(HCaptchaTaskRequest)  # type: ignore\nGeeTestTaskRequest = _decorator(GeeTestTaskRequest)  # type: ignore\n"
  },
  {
    "path": "unicaps/_service/cptch_net.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\ncptch.net service\n\"\"\"\nfrom .base import HTTPService\nfrom .._transport.http_transport import HTTPRequestJSON  # type: ignore\nfrom .. import exceptions\nfrom .._captcha import CaptchaType\nfrom ..common import CaptchaAlphabet\n\n__all__ = [\n    'Service', 'GetBalanceRequest', 'GetStatusRequest',\n    'ReportGoodRequest', 'ReportBadRequest',\n    'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',\n    'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',\n    'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest'\n]\n\n\nclass Service(HTTPService):\n    \"\"\" Main service class for 2captcha \"\"\"\n\n    BASE_URL = 'https://cptch.net'\n\n    def _post_init(self):\n        \"\"\" Init settings \"\"\"\n\n        for captcha_type in self.settings:\n            self.settings[captcha_type].polling_interval = 5\n            self.settings[captcha_type].solution_timeout = 180\n            if captcha_type in (CaptchaType.RECAPTCHAV2,):\n                self.settings[captcha_type].polling_delay = 20\n                self.settings[captcha_type].solution_timeout = 300\n            elif captcha_type in (CaptchaType.RECAPTCHAV3,):\n                self.settings[captcha_type].polling_delay = 15\n            else:\n                self.settings[captcha_type].polling_delay = 5\n\n\nclass Request(HTTPRequestJSON):\n    \"\"\" Common Request class for 2captcha \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and checks for errors \"\"\"\n\n        response_data = super().parse_response(response)\n\n        if response_data.pop(\"status\") == 1:\n            return response_data\n\n        ###############\n        # handle errors\n        ###############\n        error_code = response_data[\"request\"]\n        error_text = response_data.get(\"error_text\", \"\")\n        error_msg = f\"{error_code}: {error_text}\"\n\n        if error_code == 'CAPCHA_NOT_READY':  # pylint: disable=no-else-raise\n            raise exceptions.SolutionNotReadyYet()\n        elif error_code in ('ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST',\n                            'ERROR_IP_NOT_ALLOWED', 'IP_BANNED'):\n            raise exceptions.AccessDeniedError(error_msg)\n        elif error_code in ('ERROR_ZERO_BALANCE',):\n            raise exceptions.LowBalanceError(error_msg)\n        elif error_code in ('ERROR_NO_SLOT_AVAILABLE',):\n            # If server returns ERROR_NO_SLOT_AVAILABLE make a 5 seconds timeout before sending\n            # next request.\n            # time.sleep(5)\n            raise exceptions.ServiceTooBusy(error_msg)\n        elif error_code in ('MAX_USER_TURN',) or error_code.startswith('ERROR:'):\n            raise exceptions.TooManyRequestsError(error_msg)\n        elif error_code in ('ERROR_WRONG_ID_FORMAT', 'ERROR_WRONG_CAPTCHA_ID'):\n            raise exceptions.MalformedRequestError(error_msg)\n        elif error_code in ('ERROR_ZERO_CAPTCHA_FILESIZE', 'ERROR_TOO_BIG_CAPTCHA_FILESIZE',\n                            'ERROR_WRONG_FILE_EXTENSION', 'ERROR_IMAGE_TYPE_NOT_SUPPORTED',\n                            'ERROR_UPLOAD', 'ERROR_PAGEURL', 'ERROR_BAD_TOKEN_OR_PAGEURL',\n                            'ERROR_GOOGLEKEY', 'ERROR_BAD_PARAMETERS', 'ERROR_TOKEN_EXPIRED',\n                            'ERROR_EMPTY_ACTION', 'ERROR'):\n            raise exceptions.BadInputDataError(error_msg)\n        elif error_code in ('ERROR_CAPTCHAIMAGE_BLOCKED', 'ERROR_CAPTCHA_UNSOLVABLE',\n                            'ERROR_BAD_DUPLICATES'):\n            raise exceptions.UnableToSolveError(error_msg)\n\n        raise exceptions.ServiceError(error_msg)\n\n\nclass InRequest(Request):\n    \"\"\" Request class for requests to /in.php \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(\n                method=\"POST\",\n                url=self._service.BASE_URL + \"/in.php\",\n                data=dict(\n                    key=self._service.api_key,\n                    json=1,\n                    soft_id=\"164\"\n                )\n            )\n        )\n\n        # cptch.net doesn't like headers - returns ERROR_UPLOAD\n        if 'headers' in request:\n            del request['headers']\n\n        return request\n\n\nclass ResRequest(Request):\n    \"\"\" Request class for requests to /res.php \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(\n                method=\"GET\",\n                url=self._service.BASE_URL + \"/res.php\",\n                params=dict(\n                    key=self._service.api_key,\n                    json=1\n                )\n            )\n        )\n\n        # cptch.net doesn't like headers - returns ERROR_UPLOAD\n        if 'headers' in request:\n            del request['headers']\n\n        return request\n\n\nclass GetBalanceRequest(ResRequest):\n    \"\"\" GetBalance Request class \"\"\"\n\n    def prepare(self) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare()\n        request[\"params\"].update(dict(action=\"getbalance\"))\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return balance \"\"\"\n\n        return {'balance': float(super().parse_response(response)[\"request\"])}\n\n\nclass GetStatusRequest(GetBalanceRequest):\n    \"\"\" GetStatus Request class \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return status \"\"\"\n\n        try:\n            return super().parse_response(response)\n        except exceptions.UnicapsException:\n            return {}\n\n\nclass ReportGoodRequest(ResRequest):\n    \"\"\" ReportGood Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(solved_captcha=solved_captcha)\n        request[\"params\"].update(\n            dict(\n                action=\"reportgood\",\n                id=solved_captcha.captcha_id\n            )\n        )\n        return request\n\n\nclass ReportBadRequest(ResRequest):\n    \"\"\" ReportBad Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(solved_captcha=solved_captcha)\n        request[\"params\"].update(\n            dict(\n                action=\"reportbad\",\n                id=solved_captcha.captcha_id\n            )\n        )\n        return request\n\n\nclass TaskRequest(InRequest):\n    \"\"\" Common Task Request class \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return task_id \"\"\"\n\n        response_data = super().parse_response(response)\n\n        return dict(\n            task_id=response_data.pop(\"request\"),\n            extra=response_data\n        )\n\n\nclass SolutionRequest(ResRequest):\n    \"\"\" Common Solution Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, task) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(task=task)\n        request[\"params\"].update(\n            dict(action=\"get2\", id=task.task_id)\n        )\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return solution and cost \"\"\"\n\n        response_data = super().parse_response(response)\n\n        # get solution class\n        solution_class = self.source_data['task'].captcha.get_solution_class()\n\n        # get token and captcha cost\n        token, cost = response_data[\"request\"].rsplit('|', maxsplit=1)\n\n        return dict(\n            solution=solution_class(token),\n            cost=cost,\n            extra=response_data\n        )\n\n\nclass ImageCaptchaTaskRequest(TaskRequest):\n    \"\"\" ImageCaptchaTask Request class \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        # add required params\n        request['data'].update(\n            dict(\n                method=\"base64\",\n                body=captcha.get_image_base64().decode('ascii')\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                is_phrase=('phrase', lambda v: int(bool(v))),\n                is_case_sensitive=('regsense', lambda v: int(bool(v))),\n                char_type=('numeric', lambda v: v.value),\n                is_math=('calc', lambda v: int(bool(v))),\n                min_len=('min_len', None),\n                max_len=('max_len', None),\n                alphabet=('language',\n                          lambda v: {CaptchaAlphabet.CYRILLIC: 1,\n                                     CaptchaAlphabet.LATIN: 2}.get(v, 0)),\n                language=('lang', lambda v: v.value),\n                comment=('textinstructions', None),\n            )\n        )\n\n        return request\n\n\nclass ImageCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" Image CAPTCHA solution request \"\"\"\n\n\nclass RecaptchaV2TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v2 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"userrecaptcha\",\n                googlekey=captcha.site_key,\n                pageurl=captcha.page_url,\n                invisible=int(captcha.is_invisible)\n            )\n        )\n\n        return request\n\n\nclass RecaptchaV2SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v2 solution request \"\"\"\n\n\nclass RecaptchaV3TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v3 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"userrecaptcha\",\n                version=\"v3\",\n                googlekey=captcha.site_key,\n                pageurl=captcha.page_url,\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                action=('action', None),\n                min_score=('min_score', None)\n            )\n        )\n\n        return request\n\n\nclass RecaptchaV3SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v3 solution request \"\"\"\n"
  },
  {
    "path": "unicaps/_service/deathbycaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\ndeathbycaptcha.com service\n\"\"\"\nimport json\n\nfrom .base import HTTPService\nfrom .._transport.http_transport import HTTPRequestJSON  # type: ignore\nfrom .. import exceptions\nfrom .._captcha import CaptchaType\n\n__all__ = [\n    'Service', 'GetBalanceRequest', 'GetStatusRequest',\n    'ReportGoodRequest', 'ReportBadRequest',\n    'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',\n    'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',\n    'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest',\n    'FunCaptchaTaskRequest', 'FunCaptchaSolutionRequest',\n    'HCaptchaTaskRequest', 'HCaptchaSolutionRequest'\n]\n\n\nclass Service(HTTPService):\n    \"\"\" Main service class for deathbycaptcha \"\"\"\n\n    BASE_URL = 'http://api.dbcapi.me/api'\n\n    def _post_init(self):\n        \"\"\" Init settings \"\"\"\n        self._transport.settings['handle_http_errors'] = False\n\n        for captcha_type in self.settings:\n            self.settings[captcha_type].polling_delay = 5\n            self.settings[captcha_type].polling_interval = 2\n            self.settings[captcha_type].solution_timeout = 180\n\n            if captcha_type in (CaptchaType.RECAPTCHAV2, CaptchaType.HCAPTCHA):\n                self.settings[captcha_type].polling_delay = 15\n                self.settings[captcha_type].solution_timeout = 200\n            elif captcha_type in (CaptchaType.RECAPTCHAV3,):\n                self.settings[captcha_type].polling_delay = 15\n\n\nclass Request(HTTPRequestJSON):\n    \"\"\" Common Request class for deathbycaptcha \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare the request \"\"\"\n\n        request = super().prepare(**kwargs)\n\n        method = kwargs.get('method', 'GET')\n        data_or_params = 'data' if method == 'POST' else 'params'\n\n        request.update({\n            'method': kwargs.get('method', 'GET'),\n            'url': self._service.BASE_URL + kwargs.get('url', ''),\n            data_or_params: dict(\n                authtoken=self._service.api_key\n            )\n        })\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and check for errors \"\"\"\n\n        response_data = super().parse_response(response)\n\n        status = response_data.get('status')\n        if (response.status_code == 303 or response.is_success) and status == 0:\n            response_data.pop('status')\n            return response_data\n\n        #################\n        # handle errors #\n        #################\n        if response_data.get('error'):\n            error_text = response_data['error']\n        elif response.is_error:\n            error_text = f'[{response.status_code} {response.reason_phrase}]'\n        else:\n            error_text = 'Unknown error'\n\n        error_msg = f\"{status}: {error_text}\"\n\n        if error_text in ('token authentication disabled', 'not-logged-in', 'banned'):\n            raise exceptions.AccessDeniedError(error_msg)\n        if error_text in ('insufficient-funds',):\n            raise exceptions.LowBalanceError(error_msg)\n        if error_text in ('service-overload',):\n            raise exceptions.ServiceTooBusy(error_msg)\n        if error_text in ('upload-failed', 'invalid-captcha'):\n            raise exceptions.MalformedRequestError(error_msg)\n        if error_text in ('ERROR_PAGEURL', 'Invalid base64-encoded CAPTCHA',\n                          'Not a (CAPTCHA) image', 'Empty CAPTCHA image', 'ERROR_GOOGLEKEY',\n                          'ERROR_PAGEURL', 'ERROR_PUBLICKEY', 'ERROR_SITEKEY', 'ERROR_ACTION',\n                          'ERROR_MIN_SCORE', 'ERROR_MIN_SCORE_NOT_FLOAT'):\n            raise exceptions.BadInputDataError(error_msg)\n        if error_text in ('ERROR_PROXYTYPE', 'ERROR_PROXY'):\n            raise exceptions.ProxyError(error_msg)\n\n        raise exceptions.ServiceError(error_msg)\n\n\nclass PostRequest(Request):\n    \"\"\" Request class for POST requests \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n        return super().prepare(method=\"POST\", **kwargs)\n\n\nclass GetRequest(Request):\n    \"\"\" Request class for GET requests \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n        return super().prepare(method=\"GET\", **kwargs)\n\n\nclass GetBalanceRequest(GetRequest):\n    \"\"\" GetBalance Request class \"\"\"\n\n    def prepare(self) -> dict:\n        \"\"\" Prepare request \"\"\"\n        return super().prepare(url='')\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return balance \"\"\"\n        return {\n            'balance': float(super().parse_response(response)[\"balance\"]) / 100\n        }\n\n\nclass GetStatusRequest(GetRequest):\n    \"\"\" GetStatus Request class \"\"\"\n\n    def prepare(self):\n        \"\"\" Prepare request \"\"\"\n        return super().prepare(url='/status')\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return status \"\"\"\n        try:\n            response_data = super().parse_response(response)\n            if response_data.get('is_service_overloaded'):\n                return {}\n            return response_data\n        except exceptions.UnicapsException:\n            return {}\n\n\nclass ReportGoodRequest(PostRequest):\n    \"\"\" ReportGood Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepares request \"\"\"\n        raise exceptions.UnicapsException(\n            \"Report for good CAPTCHA is not supported by the current service!\"\n        )\n\n\nclass ReportBadRequest(PostRequest):\n    \"\"\" ReportBad Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n        return super().prepare(\n            url=f'/captcha/{solved_captcha.captcha_id}/report',\n            solved_captcha=solved_captcha\n        )\n\n\nclass TaskRequest(PostRequest):\n    \"\"\" Common Task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument\n    def prepare(self, captcha, proxy, user_agent, cookies):\n        \"\"\" Prepare a request \"\"\"\n\n        request = super().prepare(\n            url='/captcha',\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return captcha_id \"\"\"\n\n        response_data = super().parse_response(response)\n\n        # raise the BadInputDataError if CAPTCHA is not correct\n        if not response_data.pop('is_correct'):\n            raise exceptions.BadInputDataError('is_correct=false')\n\n        if 'text' in response_data:\n            response_data.pop('text')\n\n        return dict(\n            task_id=response_data.pop(\"captcha\"),\n            extra=response_data\n        )\n\n\nclass SolutionRequest(GetRequest):\n    \"\"\" Common Solution Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, task) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n        return super().prepare(url=f'/captcha/{task.task_id}', task=task)\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return solution and cost \"\"\"\n\n        response_data = super().parse_response(response)\n\n        # raise the UnableToSolveError if CAPTCHA is not correct\n        if not response_data.pop('is_correct'):\n            raise exceptions.UnableToSolveError('is_correct=false')\n\n        # the empty text field means that solving is in progress\n        text = response_data.pop(\"text\")\n        if not text:\n            raise exceptions.SolutionNotReadyYet()\n\n        # get solution class and prepare a solution object\n        solution_class = self.source_data['task'].captcha.get_solution_class()\n        solution = solution_class(text)\n\n        response_data.pop(\"captcha\")\n\n        return dict(\n            solution=solution,\n            extra=response_data\n        )\n\n\nclass ImageCaptchaTaskRequest(TaskRequest):\n    \"\"\" ImageCaptchaTask Request class \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        # add required params\n        request['data'].update(dict(\n            captchafile='base64:' + captcha.get_image_base64().decode('ascii')\n        ))\n        return request\n\n\nclass ImageCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" Image CAPTCHA solution request \"\"\"\n\n\nclass RecaptchaV2TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v2 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        data = {\n            \"googlekey\": captcha.site_key,\n            \"pageurl\": captcha.page_url\n        }\n        data.update(\n            captcha.get_optional_data(\n                data_s=('data-s', None),\n            )\n        )\n\n        request['data'].update(\n            dict(\n                type=4,\n                token_params=_dumps(data, proxy)\n            )\n        )\n\n        return request\n\n\nclass RecaptchaV2SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v2 solution request \"\"\"\n\n\nclass RecaptchaV3TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v3 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        data = {\n            \"googlekey\": captcha.site_key,\n            \"pageurl\": captcha.page_url\n        }\n\n        data.update(\n            captcha.get_optional_data(\n                action=('action', None),\n                min_score=('min_score', None),\n            )\n        )\n\n        request['data'].update(\n            dict(\n                type=5,\n                token_params=_dumps(data, proxy)\n            )\n        )\n\n        return request\n\n\nclass RecaptchaV3SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v3 solution request \"\"\"\n\n\nclass FunCaptchaTaskRequest(TaskRequest):\n    \"\"\" FunCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        data = {\n            \"publickey\": captcha.public_key,\n            \"pageurl\": captcha.page_url\n        }\n\n        request['data'].update(\n            dict(\n                type=6,\n                funcaptcha_params=_dumps(data, proxy)\n            )\n        )\n\n        return request\n\n\nclass FunCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" FunCaptcha solution request \"\"\"\n\n\nclass HCaptchaTaskRequest(TaskRequest):\n    \"\"\" HCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        data = {\n            \"sitekey\": captcha.site_key,\n            \"pageurl\": captcha.page_url\n        }\n\n        request['data'].update(\n            dict(\n                type=7,\n                hcaptcha_params=_dumps(data, proxy)\n            )\n        )\n\n        return request\n\n\nclass HCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" HCaptcha solution request \"\"\"\n\n\ndef _dumps(data, proxy):\n    if proxy:\n        data.update(\n            dict(\n                proxy=proxy.get_string(including_type=True),\n                proxytype=proxy.proxy_type.value.upper()\n            )\n        )\n    return json.dumps(data)\n"
  },
  {
    "path": "unicaps/_service/rucaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nrucaptcha.com service\n\"\"\"\n\n# pylint: disable=unused-import\nfrom .twocaptcha import (\n    Service as Service2Captcha, GetBalanceRequest, GetStatusRequest,\n    ReportGoodRequest, ReportBadRequest,\n    ImageCaptchaTaskRequest, ImageCaptchaSolutionRequest,\n    RecaptchaV2TaskRequest, RecaptchaV2SolutionRequest,\n    RecaptchaV3TaskRequest, RecaptchaV3SolutionRequest,\n    TextCaptchaTaskRequest, TextCaptchaSolutionRequest,\n    FunCaptchaTaskRequest, FunCaptchaSolutionRequest,\n    KeyCaptchaTaskRequest, KeyCaptchaSolutionRequest,\n    GeeTestTaskRequest, GeeTestSolutionRequest,\n    GeeTestV4TaskRequest, GeeTestV4SolutionRequest,\n    HCaptchaTaskRequest, HCaptchaSolutionRequest,\n    CapyPuzzleTaskRequest, CapyPuzzleSolutionRequest,\n    TikTokCaptchaTaskRequest, TikTokCaptchaSolutionRequest\n)\n\n__all__ = [\n    'Service', 'GetBalanceRequest', 'GetStatusRequest',\n    'ReportGoodRequest', 'ReportBadRequest',\n    'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',\n    'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',\n    'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest',\n    'TextCaptchaTaskRequest', 'TextCaptchaSolutionRequest',\n    'FunCaptchaTaskRequest', 'FunCaptchaSolutionRequest',\n    'KeyCaptchaTaskRequest', 'KeyCaptchaSolutionRequest',\n    'GeeTestTaskRequest', 'GeeTestSolutionRequest',\n    'GeeTestV4TaskRequest', 'GeeTestV4SolutionRequest',\n    'HCaptchaTaskRequest', 'HCaptchaSolutionRequest',\n    'CapyPuzzleTaskRequest', 'CapyPuzzleSolutionRequest',\n    'TikTokCaptchaTaskRequest', 'TikTokCaptchaSolutionRequest'\n]\n\n\nclass Service(Service2Captcha):\n    \"\"\" Main service class for rucaptcha \"\"\"\n\n    BASE_URL = 'https://rucaptcha.com'\n"
  },
  {
    "path": "unicaps/_service/twocaptcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\n2captcha.com service\n\"\"\"\n\nfrom .base import HTTPService\nfrom .._transport.http_transport import HTTPRequestJSON  # type: ignore\nfrom .. import exceptions\nfrom .._captcha import CaptchaType\nfrom ..common import CaptchaAlphabet\n\n__all__ = [\n    'Service', 'GetBalanceRequest', 'GetStatusRequest',\n    'ReportGoodRequest', 'ReportBadRequest',\n    'ImageCaptchaTaskRequest', 'ImageCaptchaSolutionRequest',\n    'RecaptchaV2TaskRequest', 'RecaptchaV2SolutionRequest',\n    'RecaptchaV3TaskRequest', 'RecaptchaV3SolutionRequest',\n    'TextCaptchaTaskRequest', 'TextCaptchaSolutionRequest',\n    'FunCaptchaTaskRequest', 'FunCaptchaSolutionRequest',\n    'KeyCaptchaTaskRequest', 'KeyCaptchaSolutionRequest',\n    'GeeTestTaskRequest', 'GeeTestSolutionRequest',\n    'GeeTestV4TaskRequest', 'GeeTestV4SolutionRequest',\n    'HCaptchaTaskRequest', 'HCaptchaSolutionRequest',\n    'CapyPuzzleTaskRequest', 'CapyPuzzleSolutionRequest',\n    'TikTokCaptchaTaskRequest', 'TikTokCaptchaSolutionRequest'\n]\n\n\nclass Service(HTTPService):\n    \"\"\" Main service class for 2captcha \"\"\"\n\n    BASE_URL = 'https://2captcha.com'\n\n    def _post_init(self):\n        \"\"\" Init settings \"\"\"\n\n        for captcha_type in self.settings:\n            self.settings[captcha_type].polling_delay = 5\n            self.settings[captcha_type].polling_interval = 5\n            self.settings[captcha_type].solution_timeout = 180\n\n            if captcha_type in (CaptchaType.RECAPTCHAV2, CaptchaType.HCAPTCHA):\n                self.settings[captcha_type].polling_delay = 20\n                self.settings[captcha_type].solution_timeout = 300\n            elif captcha_type in (CaptchaType.RECAPTCHAV3,):\n                self.settings[captcha_type].polling_delay = 15\n            elif captcha_type in (CaptchaType.TIKTOK,):\n                self.settings[captcha_type].polling_interval = 1\n\n\nclass Request(HTTPRequestJSON):\n    \"\"\" Common Request class for 2captcha \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and checks for errors \"\"\"\n\n        response_data = super().parse_response(response)\n\n        if response_data.pop(\"status\") == 1:\n            return response_data\n\n        ###############\n        # handle errors\n        ###############\n        error_code = response_data[\"request\"]\n        error_text = response_data.get(\"error_text\", \"\")\n        error_msg = f\"{error_code}: {error_text}\"\n\n        if error_code == 'CAPCHA_NOT_READY':  # pylint: disable=no-else-raise\n            raise exceptions.SolutionNotReadyYet()\n        elif error_code in ('ERROR_WRONG_USER_KEY', 'ERROR_KEY_DOES_NOT_EXIST',\n                            'ERROR_IP_NOT_ALLOWED', 'IP_BANNED'):\n            raise exceptions.AccessDeniedError(error_msg)\n        elif error_code in ('ERROR_ZERO_BALANCE',):\n            raise exceptions.LowBalanceError(error_msg)\n        elif error_code in ('ERROR_NO_SLOT_AVAILABLE',):\n            # If server returns ERROR_NO_SLOT_AVAILABLE make a 5 seconds timeout before sending\n            # next request.\n            # time.sleep(5)\n            raise exceptions.ServiceTooBusy(error_msg)\n        elif error_code in ('MAX_USER_TURN',) or error_code.startswith('ERROR:'):\n            raise exceptions.TooManyRequestsError(error_msg)\n        elif error_code in ('ERROR_WRONG_ID_FORMAT', 'ERROR_WRONG_CAPTCHA_ID'):\n            raise exceptions.MalformedRequestError(error_msg)\n        elif error_code in ('ERROR_ZERO_CAPTCHA_FILESIZE', 'ERROR_TOO_BIG_CAPTCHA_FILESIZE',\n                            'ERROR_WRONG_FILE_EXTENSION', 'ERROR_IMAGE_TYPE_NOT_SUPPORTED',\n                            'ERROR_UPLOAD', 'ERROR_PAGEURL', 'ERROR_BAD_TOKEN_OR_PAGEURL',\n                            'ERROR_GOOGLEKEY', 'ERROR_BAD_PARAMETERS', 'ERROR_TOKEN_EXPIRED',\n                            'ERROR_EMPTY_ACTION'):\n            raise exceptions.BadInputDataError(error_msg)\n        elif error_code in ('ERROR_CAPTCHAIMAGE_BLOCKED', 'ERROR_CAPTCHA_UNSOLVABLE',\n                            'ERROR_BAD_DUPLICATES'):\n            raise exceptions.UnableToSolveError(error_msg)\n        elif error_code in ('ERROR_BAD_PROXY', 'ERROR_PROXY_CONNECTION_FAILED'):\n            raise exceptions.ProxyError(error_msg)\n\n        raise exceptions.ServiceError(error_msg)\n\n\nclass InRequest(Request):\n    \"\"\" Request class for requests to /in.php \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(\n                method=\"POST\",\n                url=self._service.BASE_URL + \"/in.php\",\n                data=dict(\n                    key=self._service.api_key,\n                    json=1,\n                    soft_id=2738\n                )\n            )\n        )\n        return request\n\n\nclass ResRequest(Request):\n    \"\"\" Request class for requests to /res.php \"\"\"\n\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(\n                method=\"GET\",\n                url=self._service.BASE_URL + \"/res.php\",\n                params=dict(\n                    key=self._service.api_key,\n                    json=1\n                )\n            )\n        )\n        return request\n\n\nclass GetBalanceRequest(ResRequest):\n    \"\"\" GetBalance Request class \"\"\"\n\n    def prepare(self) -> dict:\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare()\n        request[\"params\"].update(dict(action=\"getbalance\"))\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return balance \"\"\"\n\n        return {'balance': float(super().parse_response(response)[\"request\"])}\n\n\nclass GetStatusRequest(GetBalanceRequest):\n    \"\"\" GetStatus Request class \"\"\"\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return status \"\"\"\n\n        try:\n            return super().parse_response(response)\n        except exceptions.UnicapsException:\n            return {}\n\n\nclass ReportGoodRequest(ResRequest):\n    \"\"\" ReportGood Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(solved_captcha=solved_captcha)\n        request[\"params\"].update(\n            dict(\n                action=\"reportgood\",\n                id=solved_captcha.captcha_id\n            )\n        )\n        return request\n\n\nclass ReportBadRequest(ResRequest):\n    \"\"\" ReportBad Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, solved_captcha) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(solved_captcha=solved_captcha)\n        request[\"params\"].update(\n            dict(\n                action=\"reportbad\",\n                id=solved_captcha.captcha_id\n            )\n        )\n        return request\n\n\nclass TaskRequest(InRequest):\n    \"\"\" Common Task Request class \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument\n    def prepare(self, captcha, proxy, user_agent, cookies):\n        \"\"\" Prepare a request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        if proxy:\n            request['data'].update(\n                dict(\n                    proxy=proxy.get_string(),\n                    proxytype=proxy.proxy_type.value.upper()\n                )\n            )\n\n        if cookies:\n            request['data']['cookies'] = ';'.join([f'{k}:{v}' for k, v in cookies.items()])\n\n        if user_agent:\n            request['data']['userAgent'] = user_agent\n\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return task_id \"\"\"\n\n        response_data = super().parse_response(response)\n\n        return dict(\n            task_id=response_data.pop(\"request\"),\n            extra=response_data\n        )\n\n\nclass SolutionRequest(ResRequest):\n    \"\"\" Common Solution Request class \"\"\"\n\n    # pylint: disable=arguments-differ\n    def prepare(self, task) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(task=task)\n        request[\"params\"].update(\n            dict(action=\"get2\", id=task.task_id)\n        )\n        return request\n\n    def parse_response(self, response) -> dict:\n        \"\"\" Parse response and return solution and cost \"\"\"\n\n        response_data = super().parse_response(response)\n\n        solution_data = response_data.pop(\"request\")\n        solution_class = self.source_data['task'].captcha.get_solution_class()\n        captcha_type = self.source_data['task'].captcha.get_type()\n        if captcha_type == CaptchaType.GEETEST:\n            solution = solution_class(\n                challenge=solution_data['geetest_challenge'],\n                validate=solution_data['geetest_validate'],\n                seccode=solution_data['geetest_seccode']\n            )\n        elif captcha_type == CaptchaType.GEETESTV4:\n            solution = solution_class(\n                captcha_id=solution_data['captcha_id'],\n                lot_number=solution_data['lot_number'],\n                pass_token=solution_data['pass_token'],\n                gen_time=solution_data['gen_time'],\n                captcha_output=solution_data['captcha_output'],\n            )\n        elif captcha_type == CaptchaType.CAPY:\n            solution = solution_class(**solution_data)\n        elif captcha_type in (CaptchaType.TIKTOK,):\n            solution = solution_class(\n                dict(\n                    [key_value.split(':', maxsplit=1) for key_value in solution_data.split(';')]\n                )\n            )\n        else:\n            solution = solution_class(solution_data)\n\n        price = None\n        if 'price' in response_data:\n            price = response_data.pop(\"price\")\n\n        return dict(\n            solution=solution,\n            cost=price,\n            extra=response_data\n        )\n\n\nclass ImageCaptchaTaskRequest(TaskRequest):\n    \"\"\" ImageCaptchaTask Request class \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        # add required params\n        request['data'].update(\n            dict(\n                method=\"base64\",\n                body=captcha.get_image_base64().decode('ascii')\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                is_phrase=('phrase', lambda v: int(bool(v))),\n                is_case_sensitive=('regsense', lambda v: int(bool(v))),\n                char_type=('numeric', lambda v: v.value),\n                is_math=('calc', lambda v: int(bool(v))),\n                min_len=('min_len', None),\n                max_len=('max_len', None),\n                alphabet=('language',\n                          lambda v: {CaptchaAlphabet.CYRILLIC: 1,\n                                     CaptchaAlphabet.LATIN: 2}.get(v, 0)),\n                language=('lang', lambda v: v.value),\n                comment=('textinstructions', None),\n            )\n        )\n\n        return request\n\n\nclass ImageCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" Image CAPTCHA solution request \"\"\"\n\n\nclass RecaptchaV2TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v2 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n        request['data'].update(\n            dict(\n                method=\"userrecaptcha\",\n                googlekey=captcha.site_key,\n                pageurl=captcha.page_url,\n                invisible=int(captcha.is_invisible)\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                data_s=('data-s', None),\n                api_domain=('domain', None)\n            )\n        )\n\n        # check if enterprise captcha\n        if captcha.is_enterprise:\n            request['data']['enterprise'] = 1\n\n        return request\n\n\nclass RecaptchaV2SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v2 solution request \"\"\"\n\n\nclass RecaptchaV3TaskRequest(TaskRequest):\n    \"\"\" reCAPTCHA v3 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n        request['data'].update(\n            dict(\n                method=\"userrecaptcha\",\n                version=\"v3\",\n                googlekey=captcha.site_key,\n                pageurl=captcha.page_url\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                action=('action', None),\n                min_score=('min_score', None),\n                api_domain=('domain', None)\n            )\n        )\n\n        # check if enterprise captcha\n        if captcha.is_enterprise:\n            request['data']['enterprise'] = 1\n\n        return request\n\n\nclass RecaptchaV3SolutionRequest(SolutionRequest):\n    \"\"\" reCAPTCHA v3 solution request \"\"\"\n\n\nclass TextCaptchaTaskRequest(TaskRequest):\n    \"\"\" TextCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,unused-argument,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                textcaptcha=captcha.text\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                alphabet=('language',\n                          lambda v: {CaptchaAlphabet.CYRILLIC: 1,\n                                     CaptchaAlphabet.LATIN: 2}.get(v, 0)),\n                language=('lang', lambda v: v.value)\n            )\n        )\n\n        return request\n\n\nclass TextCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" TextCaptcha solution request \"\"\"\n\n\nclass FunCaptchaTaskRequest(TaskRequest):\n    \"\"\" FunCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"funcaptcha\",\n                publickey=captcha.public_key,\n                pageurl=captcha.page_url\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                service_url=('surl', None),\n                no_js=('nojs', None),\n                blob=('data[blob]', None),\n            )\n        )\n\n        return request\n\n\nclass FunCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" FunCaptcha solution request \"\"\"\n\n\nclass KeyCaptchaTaskRequest(TaskRequest):\n    \"\"\" KeyCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"keycaptcha\",\n                s_s_c_user_id=captcha.user_id,\n                s_s_c_session_id=captcha.session_id,\n                s_s_c_web_server_sign=captcha.ws_sign,\n                s_s_c_web_server_sign2=captcha.ws_sign2,\n                pageurl=captcha.page_url\n            )\n        )\n\n        return request\n\n\nclass KeyCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" KeyCaptcha solution request \"\"\"\n\n\nclass GeeTestTaskRequest(TaskRequest):\n    \"\"\" GeeTest task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"geetest\",\n                gt=captcha.gt_key,\n                challenge=captcha.challenge,\n                pageurl=captcha.page_url\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                api_server=('api_server', None)\n            )\n        )\n\n        return request\n\n\nclass GeeTestSolutionRequest(SolutionRequest):\n    \"\"\" GeeTest solution request \"\"\"\n\n\nclass HCaptchaTaskRequest(TaskRequest):\n    \"\"\" HCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"hcaptcha\",\n                sitekey=captcha.site_key,\n                pageurl=captcha.page_url,\n                invisible=int(captcha.is_invisible)\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                api_domain=('domain', None)\n            )\n        )\n\n        return request\n\n\nclass HCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" HCaptcha solution request \"\"\"\n\n\nclass CapyPuzzleTaskRequest(TaskRequest):\n    \"\"\" CapyPuzzle task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"capy\",\n                captchakey=captcha.site_key,\n                pageurl=captcha.page_url\n            )\n        )\n\n        # add optional params\n        request['data'].update(\n            captcha.get_optional_data(\n                api_server=('api_server', None)\n            )\n        )\n\n        return request\n\n\nclass CapyPuzzleSolutionRequest(SolutionRequest):\n    \"\"\" CapyPuzzle solution request \"\"\"\n\n\nclass TikTokCaptchaTaskRequest(TaskRequest):\n    \"\"\" TikTokCaptcha task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        # default values for additional params\n        # see https://2captcha.com/2captcha-api#solving_tiktok for details\n        add_params_mapping = {\n            'https://www.tiktok.com/login/phone-or-email/email': {\n                'aid': 1459,\n                'host': 'https://www-useast1a.tiktok.com'\n            },\n            'https://ads.tiktok.com/i18n/signup': {\n                'aid': 1583,\n                'host': 'https://verify-sg.byteoversea.com'\n            },\n            'default': {'aid': None, 'host': None}\n        }\n\n        add_params = add_params_mapping[next(\n            (url for url in add_params_mapping if captcha.page_url.startswith(url)),\n            'default'\n        )]\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"tiktok\",\n                pageurl=captcha.page_url,\n                aid=add_params['aid'] if captcha.aid is None else captcha.aid,\n                host=add_params['host'] if captcha.host is None else captcha.host\n            )\n        )\n\n        return request\n\n\nclass TikTokCaptchaSolutionRequest(SolutionRequest):\n    \"\"\" TikTokCaptcha solution request \"\"\"\n\n\nclass GeeTestV4TaskRequest(TaskRequest):\n    \"\"\" GeeTest v4 task request \"\"\"\n\n    # pylint: disable=arguments-differ,signature-differs\n    def prepare(self, captcha, proxy, user_agent, cookies) -> dict:  # type: ignore\n        \"\"\" Prepare request \"\"\"\n\n        request = super().prepare(\n            captcha=captcha,\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n        request['data'].update(\n            dict(\n                method=\"geetest_v4 \",\n                captcha_id=captcha.captcha_id,\n                pageurl=captcha.page_url\n            )\n        )\n\n        return request\n\n\nclass GeeTestV4SolutionRequest(SolutionRequest):\n    \"\"\" GeeTest v4 solution request \"\"\"\n"
  },
  {
    "path": "unicaps/_solver.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nCaptchaSolver class\n\"\"\"\nimport io\nimport pathlib\nfrom typing import Union\n\nfrom .captcha import (\n    ImageCaptcha, TextCaptcha, RecaptchaV2, RecaptchaV3, HCaptcha, FunCaptcha, KeyCaptcha, GeeTest,\n    GeeTestV4, CapyPuzzle, TikTokCaptcha\n)\nfrom ._captcha.base import BaseCaptcha  # type: ignore\nfrom ._service import CaptchaSolvingService, SOLVING_SERVICE\nfrom ._service.base import SolvedCaptcha, CaptchaTask\n\n\nclass CaptchaSolver:\n    \"\"\"Main captcha solver :class:`CaptchaSolver <CaptchaSolver>` object.\n\n    :param service_name: captcha solving service to use (enum CaptchaSolvingService or str).\n    :param api_key: API key to access the solving service.\n    \"\"\"\n\n    def __init__(self, service_name: Union[CaptchaSolvingService, str], api_key: str):\n        # check service_name\n        if isinstance(service_name, CaptchaSolvingService):\n            self.service_name = service_name\n        elif isinstance(service_name, str):\n            try:\n                self.service_name = CaptchaSolvingService(service_name)\n            except ValueError as exc:\n                raise ValueError(\n                    f\"'{service_name}' is not a valid CaptchaSolvingService. \"\n                    \"Please use one of the following values: \" + ', '.join(\n                        [f\"'{i.value}'\" for i in CaptchaSolvingService]\n                    )\n                ) from exc\n        else:\n            raise ValueError(\n                '\"service_name\" param must be an instance of str or CaptchaSolvingService!'\n            )\n\n        self.api_key = api_key\n        self._service = SOLVING_SERVICE[self.service_name].Service(api_key)  # type: ignore\n\n    def _solve_captcha(self, captcha_class, *args, **kwargs):\n        proxy = kwargs.pop('proxy') if 'proxy' in kwargs else None\n        user_agent = kwargs.pop('user_agent') if 'user_agent' in kwargs else None\n        cookies = kwargs.pop('cookies') if 'cookies' in kwargs else None\n\n        return self._service.solve_captcha(\n            captcha_class(*args, **kwargs),\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n    def solve_image_captcha(self,\n                            image: Union[bytes, io.RawIOBase, io.BufferedIOBase, pathlib.Path],\n                            **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves image CAPTCHA.\n\n        :param image: binary file, bytes or pathlib.Path object containing image with CAPTCHA\n        :param char_type: (optional) Character type.\n        :param is_phrase: (optional) Boolean. True if CAPTCHA contains more than one word.\n        :param is_case_sensitive: (optional) Boolean.\n        :param is_math: (optional) Boolean. True if CAPTCHA requires calculation.\n        :param min_len: (optional) Integer. Minimum length of the CAPTCHA's text.\n        :param max_len: (optional) Integer. Maximum length of the CAPTCHA's text.\n        :param alphabet: (optional) Alphabet used in the CAPTCHA.\n        :param language: (optional) Language.\n        :param comment: (optional) String. Text instructions for worker.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n\n        return self._solve_captcha(ImageCaptcha, image, **kwargs)\n\n    def solve_text_captcha(self, text: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves text CAPTCHA.\n\n        :param text: String with text captcha task.\n        :param alphabet: (optional) Alphabet used in the CAPTCHA.\n        :param language: (optional) Language.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(TextCaptcha, text, **kwargs)\n\n    def solve_recaptcha_v2(self, site_key: str, page_url: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves reCAPTCHA v2.\n\n        :param site_key: Value of \"data-sitekey\" (or \"k\") parameter.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param is_invisible: (optional) Invisible reCAPTCHA flag.\n        :param is_enterprise: (optional) reCAPTCHA Enterprise flag.\n        :param data_s: (optional) Value of \"data-s\" parameter.\n        :param api_domain: (optional) Domain used to load the captcha: google.com or recaptcha.net.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(RecaptchaV2, site_key, page_url, **kwargs)\n\n    def solve_recaptcha_v3(self, site_key: str, page_url: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves reCAPTCHA v3.\n\n        :param site_key: Value of \"render\" parameter.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param is_enterprise: (optional) reCAPTCHA Enterprise flag (default: False).\n        :param action: (optional) Widget action value.\n        :param min_score: (optional) Filters a worker with corresponding score.\n        :param api_domain: (optional) Domain used to load the captcha: google.com or recaptcha.net.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(RecaptchaV3, site_key, page_url, **kwargs)\n\n    def solve_hcaptcha(self, site_key: str, page_url: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves hCaptcha.\n\n        :param site_key: hCaptcha website key.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param is_invisible: (optional) Invisible hCaptcha flag (default: False).\n        :param api_domain: (optional) API domain: hcaptcha.com or js.hcaptcha.com.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(HCaptcha, site_key, page_url, **kwargs)\n\n    def solve_funcaptcha(self, public_key: str, page_url: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves FunCaptcha.\n\n        :param public_key: FunCaptcha public key.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param service_url: (optional) Service URL.\n        :param no_js: (optional) Disable JavaScript.\n        :param blob: (optional) The \"blob\" value of CAPTCHA.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(FunCaptcha, public_key, page_url, **kwargs)\n\n    def solve_keycaptcha(self, page_url: str, user_id: str, session_id: str, ws_sign: str,\n                         ws_sign2: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves KeyCaptcha.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param user_id: Value of \"s_s_c_user_id\" parameter.\n        :param session_id: Value of \"s_s_c_session_id\" parameter.\n        :param ws_sign: Value of \"s_s_c_web_server_sign\" parameter.\n        :param ws_sign2: Value of \"s_s_c_web_server_sign2\" parameter.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(\n            KeyCaptcha, page_url, user_id, session_id, ws_sign, ws_sign2, **kwargs\n        )\n\n    def solve_geetest(self, page_url: str, gt_key: str, challenge: str,\n                      **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves GeeTest.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param gt_key: Public website key (static).\n        :param challenge: Dynamic challenge key.\n        :param api_server: (optional) API domain\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(GeeTest, page_url, gt_key, challenge, **kwargs)\n\n    def solve_geetest_v4(self, page_url: str, captcha_id: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves GeeTestV4.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param captcha_id: Value of captcha_id parameter you found on target website.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(GeeTestV4, page_url, captcha_id, **kwargs)\n\n    def solve_capy_puzzle(self, site_key: str, page_url: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves Capy Puzzle CAPTCHA.\n\n        :param site_key: Public website key (static).\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param api_server: (optional) The domain part of script URL you found on page.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(CapyPuzzle, site_key, page_url, **kwargs)\n\n    def solve_tiktok(self, page_url: str, **kwargs) -> SolvedCaptcha:\n        r\"\"\"Solves TikTokCaptcha.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param aid: (optional) The aid parameter value for the page.\n        :param host: (optional) The host parameter value for the page.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`SolvedCaptcha <SolvedCaptcha>` object\n        :rtype: unicaps.SolvedCaptcha\n        \"\"\"\n        return self._solve_captcha(TikTokCaptcha, page_url, **kwargs)\n\n    def create_task(self, captcha: BaseCaptcha) -> CaptchaTask:\n        \"\"\"Create task to solve CAPTCHA\n\n        :param captcha: Captcha to solve.\n        :return: :class:`CaptchaTask <CaptchaTask>` object\n        :rtype: unicaps.CaptchaTask\n        \"\"\"\n        return self._service.create_task(captcha)\n\n    def get_balance(self) -> float:\n        \"\"\"Get account balance\n\n        :return: :float:Balance amount\n        :rtype: float\n        \"\"\"\n        return self._service.get_balance()\n\n    def get_status(self) -> bool:\n        \"\"\"Get service status\n\n        :return: :bool:Service status\n        :rtype: bool\n        \"\"\"\n        return self._service.get_status()\n\n    def close(self) -> None:\n        \"\"\"Close all connections\"\"\"\n        self._service.close()\n\n    def __enter__(self):\n        return self\n\n    def __exit__(self, exc_type, exc_value, traceback):\n        self.close()\n"
  },
  {
    "path": "unicaps/_solver_async.py",
    "content": "\"\"\"\nAsyncCaptchaSolver class\n\"\"\"\nimport io\nimport pathlib\nfrom typing import Union\n\nfrom .captcha import (\n    ImageCaptcha, TextCaptcha, RecaptchaV2, RecaptchaV3, HCaptcha, FunCaptcha, KeyCaptcha, GeeTest,\n    GeeTestV4, CapyPuzzle, TikTokCaptcha\n)\nfrom ._captcha.base import BaseCaptcha  # type: ignore\nfrom ._service.base import AsyncSolvedCaptcha, AsyncCaptchaTask\nfrom ._solver import CaptchaSolver\n\n\nclass AsyncCaptchaSolver(CaptchaSolver):\n    \"\"\"Main captcha solver :class:`AsyncCaptchaSolver <AsyncCaptchaSolver>` object.\n\n    :param service_name: captcha solving service to use (enum CaptchaSolvingService or str).\n    :param api_key: API key to access the solving service.\n    \"\"\"\n\n    async def _solve_captcha_async(self, captcha_class, *args, **kwargs):\n        proxy = kwargs.pop('proxy') if 'proxy' in kwargs else None\n        user_agent = kwargs.pop('user_agent') if 'user_agent' in kwargs else None\n        cookies = kwargs.pop('cookies') if 'cookies' in kwargs else None\n\n        return await self._service.solve_captcha_async(\n            captcha_class(*args, **kwargs),\n            proxy=proxy,\n            user_agent=user_agent,\n            cookies=cookies\n        )\n\n    async def solve_image_captcha(self,  # type: ignore\n                                  image: Union[bytes, io.RawIOBase, io.BufferedIOBase,\n                                               pathlib.Path],\n                                  **kwargs) -> AsyncSolvedCaptcha:  # type: ignore\n        r\"\"\"Solves image CAPTCHA.\n\n        :param image: binary file, bytes or pathlib.Path object containing image with CAPTCHA\n        :param char_type: (optional) Character type.\n        :param is_phrase: (optional) Boolean. True if CAPTCHA contains more than one word.\n        :param is_case_sensitive: (optional) Boolean.\n        :param is_math: (optional) Boolean. True if CAPTCHA requires calculation.\n        :param min_len: (optional) Integer. Minimum length of the CAPTCHA's text.\n        :param max_len: (optional) Integer. Maximum length of the CAPTCHA's text.\n        :param alphabet: (optional) Alphabet used in the CAPTCHA.\n        :param language: (optional) Language.\n        :param comment: (optional) String. Text instructions for worker.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n\n        return await self._solve_captcha_async(ImageCaptcha, image, **kwargs)\n\n    async def solve_text_captcha(self, text: str, **kwargs) -> AsyncSolvedCaptcha:  # type: ignore\n        r\"\"\"Solves text CAPTCHA.\n\n        :param text: String with text captcha task.\n        :param alphabet: (optional) Alphabet used in the CAPTCHA.\n        :param language: (optional) Language.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(TextCaptcha, text, **kwargs)\n\n    async def solve_recaptcha_v2(self, site_key: str, page_url: str,  # type: ignore\n                                 **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves reCAPTCHA v2.\n\n        :param site_key: Value of \"data-sitekey\" (or \"k\") parameter.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param is_invisible: (optional) Invisible reCAPTCHA flag.\n        :param is_enterprise: (optional) reCAPTCHA Enterprise flag.\n        :param data_s: (optional) Value of \"data-s\" parameter.\n        :param api_domain: (optional) Domain used to load the captcha: google.com or recaptcha.net.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(RecaptchaV2, site_key, page_url, **kwargs)\n\n    async def solve_recaptcha_v3(self, site_key: str, page_url: str,  # type: ignore\n                                 **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves reCAPTCHA v3.\n\n        :param site_key: Value of \"render\" parameter.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param is_enterprise: (optional) reCAPTCHA Enterprise flag.\n        :param action: (optional) Widget action value.\n        :param min_score: (optional) Filters a worker with corresponding score.\n        :param api_domain: (optional) Domain used to load the captcha: google.com or recaptcha.net.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(RecaptchaV3, site_key, page_url, **kwargs)\n\n    async def solve_hcaptcha(self, site_key: str, page_url: str,  # type: ignore\n                             **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves hCaptcha.\n\n        :param site_key: hCaptcha website key.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param is_invisible: (optional) Invisible hCaptcha flag (default: False).\n        :param api_domain: (optional) API domain: hcaptcha.com or js.hcaptcha.com.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(HCaptcha, site_key, page_url, **kwargs)\n\n    async def solve_funcaptcha(self, public_key: str, page_url: str,  # type: ignore\n                               **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves FunCaptcha.\n\n        :param public_key: FunCaptcha public key.\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param service_url: (optional) Service URL.\n        :param no_js: (optional) Disable JavaScript.\n        :param blob: (optional) The \"blob\" value of CAPTCHA.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(FunCaptcha, public_key, page_url, **kwargs)\n\n    async def solve_keycaptcha(self, page_url: str, user_id: str, session_id: str,   # type: ignore\n                               ws_sign: str, ws_sign2: str, **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves KeyCaptcha.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param user_id: Value of \"s_s_c_user_id\" parameter.\n        :param session_id: Value of \"s_s_c_session_id\" parameter.\n        :param ws_sign: Value of \"s_s_c_web_server_sign\" parameter.\n        :param ws_sign2: Value of \"s_s_c_web_server_sign2\" parameter.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(\n            KeyCaptcha, page_url, user_id, session_id, ws_sign, ws_sign2, **kwargs\n        )\n\n    async def solve_geetest(self, page_url: str, gt_key: str, challenge: str,  # type: ignore\n                            **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves GeeTest.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param gt_key: Public website key (static).\n        :param challenge: Dynamic challenge key.\n        :param api_server: (optional) API domain\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(GeeTest, page_url, gt_key, challenge, **kwargs)\n\n    async def solve_geetest_v4(self, page_url: str, captcha_id: str,  # type: ignore\n                               **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves GeeTestV4.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param captcha_id: Value of captcha_id parameter you found on target website.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(GeeTestV4, page_url, captcha_id, **kwargs)\n\n    async def solve_capy_puzzle(self, site_key: str, page_url: str,  # type: ignore\n                                **kwargs) -> AsyncSolvedCaptcha:\n        r\"\"\"Solves Capy.\n\n        :param site_key: Public website key (static).\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param api_server: (optional) The domain part of script URL you found on page.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(CapyPuzzle, site_key, page_url, **kwargs)\n\n    async def solve_tiktok(self, page_url: str, **kwargs) -> AsyncSolvedCaptcha:  # type: ignore\n        r\"\"\"Solves TikTokCaptcha.\n\n        :param page_url: Full URL of the page with CAPTCHA.\n        :param aid: (optional) The aid parameter value for the page.\n        :param host: (optional) The host parameter value for the page.\n        :param proxy: (optional) Proxy to use while solving the CAPTCHA.\n        :param user_agent: (optional) User-Agent to use while solving the CAPTCHA.\n        :param cookies: (optional) Cookies to use while solving the CAPTCHA.\n        :return: :class:`AsyncSolvedCaptcha <AsyncSolvedCaptcha>` object\n        :rtype: unicaps.AsyncSolvedCaptcha\n        \"\"\"\n        return await self._solve_captcha_async(TikTokCaptcha, page_url, **kwargs)\n\n    async def create_task(self, captcha: BaseCaptcha) -> AsyncCaptchaTask:  # type: ignore\n        \"\"\"Create task to solve CAPTCHA\n\n        :param captcha: Captcha to solve.\n        :return: :class:`AsyncCaptchaTask <AsyncCaptchaTask>` object\n        :rtype: unicaps.AsyncCaptchaTask\n        \"\"\"\n        return await self._service.create_task_async(captcha)\n\n    async def get_balance(self) -> float:  # type: ignore\n        \"\"\"Get account balance\n\n        :return: :float:Balance amount\n        :rtype: float\n        \"\"\"\n        return await self._service.get_balance_async()\n\n    async def get_status(self) -> bool:  # type: ignore\n        \"\"\"Get service status\n\n        :return: :bool:Service status\n        :rtype: bool\n        \"\"\"\n        return await self._service.get_status_async()\n\n    async def close(self) -> None:  # type: ignore\n        \"\"\"Close all connections\"\"\"\n        await self._service.close_async()\n\n    async def __aenter__(self):\n        return self\n\n    async def __aexit__(self, exc_type, exc_value, traceback):\n        await self.close()\n"
  },
  {
    "path": "unicaps/_transport/__init__.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nTransport related stuff\n\"\"\"\n\nfrom .http_transport import StandardHTTPTransport, HTTPRequestJSON\n\n__all__ = 'StandardHTTPTransport', 'HTTPRequestJSON'\n"
  },
  {
    "path": "unicaps/_transport/base.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nBase transport stuff\n\"\"\"\n\nfrom abc import ABC, abstractmethod\nfrom typing import Optional, Any\n\n\nclass BaseRequest(ABC):\n    \"\"\" Base request class \"\"\"\n\n    def __init__(self, service):\n        # solving service instance\n        self._service = service\n        # source request data (not None if a request in process)\n        self.source_data = None\n\n    @abstractmethod\n    def prepare(self, **kwargs) -> dict:\n        \"\"\" Prepare request data \"\"\"\n        self.source_data = kwargs\n        return {}\n\n    @abstractmethod\n    def parse_response(self, response: Any) -> dict:\n        \"\"\" Parse response \"\"\"\n        if self.source_data is None:\n            raise RuntimeError('The Request.prepare() method must be called first!')\n        return {}\n\n    def process_response(self, response: Any) -> dict:\n        \"\"\" Parse response and clean source request data \"\"\"\n        response = self.parse_response(response)\n        self.source_data = None\n        return response\n\n\nclass BaseTransport(ABC):  # pylint: disable=too-few-public-methods\n    \"\"\" Base transport class \"\"\"\n\n    def __init__(self, settings: Optional[dict] = None):\n        self.settings = settings or {}\n\n    @abstractmethod\n    def _make_request(self, request_data: dict) -> Any:\n        \"\"\" Abstract method to make a request \"\"\"\n\n    @abstractmethod\n    async def _make_request_async(self, request_data: dict) -> Any:\n        \"\"\" Abstract method to make a request \"\"\"\n\n    def make_request(self, request: BaseRequest, *args) -> dict:\n        \"\"\" Makes a request to the service \"\"\"\n        response = self._make_request(request.prepare(*args))\n        return request.process_response(response)\n\n    async def make_request_async(self, request: BaseRequest, *args) -> dict:\n        \"\"\" Makes a request to the service \"\"\"\n        response = await self._make_request_async(request.prepare(*args))\n        return request.process_response(response)\n\n    @abstractmethod\n    def close(self):\n        \"\"\" Close connections \"\"\"\n\n    @abstractmethod\n    async def close_async(self):\n        \"\"\" Close connections (async) \"\"\"\n"
  },
  {
    "path": "unicaps/_transport/http_transport.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nTransport and requests for HTTP protocol\n\"\"\"\n\nfrom json.decoder import JSONDecodeError\nfrom typing import Optional, Dict\n\nimport httpx\n\nfrom .base import BaseTransport, BaseRequest  # type: ignore\nfrom ..exceptions import NetworkError, ServiceError  # type: ignore\nfrom ..__version__ import __version__  # type: ignore\n\n\nHTTP_RETRY_MAX_COUNT = 5  # max retry count in case of http(s) errors\nHTTP_RETRY_BACKOFF_FACTOR = 0.5  # backoff factor for Retry\nHTTP_RETRY_STATUS_FORCELIST = {500, 502, 503, 504}  # status forcelist for Retry\n\n\nclass StandardHTTPTransport(BaseTransport):  # pylint: disable=too-few-public-methods\n    \"\"\" Standard HTTP Transport \"\"\"\n\n    def __init__(self, settings: Optional[Dict] = None):\n        super().__init__(settings)\n        self.settings.setdefault('max_retries', HTTP_RETRY_MAX_COUNT)\n        self.settings.setdefault('handle_http_errors', True)\n\n        default_headers = {\n            'User-Agent': f'python-unicaps/{__version__}'\n        }\n\n        self.session = httpx.Client(\n            headers=default_headers,\n            timeout=httpx.Timeout(timeout=30)\n        )\n        self.session_async = httpx.AsyncClient(\n            headers=default_headers,\n            timeout=httpx.Timeout(timeout=30)\n        )\n\n    def _make_request(self, request_data: Dict) -> httpx.Response:\n        if 'headers' not in request_data:\n            request_data['headers'] = {}\n\n        try:\n            response = self.session.request(**request_data)\n        except httpx.TimeoutException as exc:\n            raise NetworkError('Timeout') from exc\n        except httpx.RequestError as exc:\n            raise NetworkError('RequestError') from exc\n\n        if self.settings['handle_http_errors']:\n            try:\n                response.raise_for_status()\n            except httpx.HTTPStatusError as exc:\n                raise NetworkError('HTTPStatusError') from exc\n\n        return response\n\n    async def _make_request_async(self, request_data: Dict) -> httpx.Response:\n        if 'headers' not in request_data:\n            request_data['headers'] = {}\n\n        try:\n            response = await self.session_async.request(**request_data)\n        except httpx.TimeoutException as exc:\n            raise NetworkError('Timeout') from exc\n        except httpx.RequestError as exc:\n            raise NetworkError('RequestError') from exc\n\n        if self.settings['handle_http_errors']:\n            try:\n                response.raise_for_status()\n            except httpx.HTTPStatusError as exc:\n                raise NetworkError('HTTPStatusError') from exc\n\n        return response\n\n    def close(self):\n        \"\"\" Close connections \"\"\"\n        self.session.close()\n\n    async def close_async(self):\n        \"\"\" Close connections (async) \"\"\"\n        await self.session_async.aclose()\n\n\nclass HTTPRequestJSON(BaseRequest):\n    \"\"\" HTTP Request that returns JSON response \"\"\"\n\n    def prepare(self, **kwargs) -> Dict:\n        \"\"\" Prepares request \"\"\"\n\n        request = super().prepare(**kwargs)\n        request.update(\n            dict(headers={'Accept': 'application/json'})\n        )\n        return request\n\n    def parse_response(self, response: httpx.Response) -> Dict:\n        \"\"\" Parses response \"\"\"\n\n        try:\n            return response.json()\n        except JSONDecodeError as exc:\n            raise ServiceError(\"Unable to parse response from the server: bad JSON\") from exc\n"
  },
  {
    "path": "unicaps/captcha.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nSupported CAPTCHAs\n~~~~~~~~~~~~~~~~~~\n\"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom ._captcha import (ImageCaptcha, TextCaptcha, RecaptchaV2, RecaptchaV3, HCaptcha, FunCaptcha,\n                       KeyCaptcha, GeeTest, GeeTestV4, CapyPuzzle, TikTokCaptcha, CaptchaType)\n\n__all__ = (\n    'ImageCaptcha',\n    'TextCaptcha',\n    'RecaptchaV2',\n    'RecaptchaV3',\n    'HCaptcha',\n    'FunCaptcha',\n    'KeyCaptcha',\n    'GeeTest',\n    'GeeTestV4',\n    'CapyPuzzle',\n    'TikTokCaptcha',\n    'CaptchaType'\n)\n"
  },
  {
    "path": "unicaps/common.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nCAPTCHA common stuff\n\"\"\"\n\nimport enum\n\n\nclass CaptchaAlphabet(enum.Enum):\n    \"\"\" Alphabet used in the CAPTCHA \"\"\"\n\n    LATIN = 'latin'\n    CYRILLIC = 'cyrillic'\n\n\nclass CaptchaCharType(enum.Enum):\n    \"\"\" Character types used in CAPTCHA \"\"\"\n\n    NUMERIC = 1\n    ALPHA = 2\n    ALPHA_OR_NUMERIC = 3\n    ALPHANUMERIC = 4\n\n\nclass WorkerLanguage(enum.Enum):\n    \"\"\" Worker's language to solve the CAPTCHA \"\"\"\n\n    ENGLISH = 'en'\n    RUSSIAN = 'ru'\n    SPANISH = 'es'\n    PORTUGUESE = 'pt'\n    UKRAINIAN = 'uk'\n    VIETNAMESE = 'vi'\n    FRENCH = 'fr'\n    INDONESIAN = 'id'\n    ARAB = 'ar'\n    JAPANESE = 'ja'\n    TURKISH = 'tr'\n    GERMAN = 'de'\n    CHINESE = 'zh'\n    # PHILIPPINE = 'fil'\n    POLISH = 'pl'\n    THAI = 'th'\n    ITALIAN = 'it'\n    DUTCH = 'nl'\n    SLOVAK = 'sk'\n    BULGARIAN = 'bg'\n    ROMANIAN = 'ro'\n    HUNGARIAN = 'hu'\n    KOREAN = 'ko'\n    CZECH = 'cs'\n    AZERBAIJANI = 'az'\n    PERSIAN = 'fa'\n    BENGALI = 'bn'\n    GREEK = 'el'\n    LITHUANIAN = 'lt'\n    LATVIAN = 'lv'\n    SWEDISH = 'sv'\n    SERBIAN = 'sr'\n    CROATIAN = 'hr'\n    HEBREW = 'he'\n    HINDI = 'hi'\n    NORWEGIAN = 'nb'\n    SLOVENIAN = 'sl'\n    DANISH = 'da'\n    UZBEK = 'uz'\n    FINNISH = 'fi'\n    CATALAN = 'ca'\n    GEORGIAN = 'ka'\n    MALAY = 'ms'\n    TELUGU = 'te'\n    ESTONIAN = 'et'\n    MALAYALAM = 'ml'\n    BELORUSSIAN = 'be'\n    KAZAKH = 'kk'\n    MARATHI = 'mr'\n    NEPALI = 'ne'\n    BURMESE = 'my'\n    BOSNIAN = 'bs'\n    ARMENIAN = 'hy'\n    MACEDONIAN = 'mk'\n    PUNJABI = 'pa'\n"
  },
  {
    "path": "unicaps/exceptions.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nunicaps.exceptions\n~~~~~~~~~~~~~~~~~~~\n\nThis module contains the set of Unicaps' exceptions.\n\"\"\"\n\n\nclass UnicapsException(Exception):\n    \"\"\"Main exception class\"\"\"\n\n\nclass SolutionNotReadyYet(UnicapsException):\n    \"\"\"CAPTCHA solving in progress\"\"\"\n\n\nclass ServiceError(UnicapsException):\n    \"\"\"Main service-related exception class\"\"\"\n\n\nclass CaptchaError(UnicapsException):\n    \"\"\"CAPTCHA-related exception\"\"\"\n\n\nclass NetworkError(UnicapsException):\n    \"\"\"\n    Network Connection Error\n    Service returned 5xx status code\n    \"\"\"\n\n\nclass ProxyError(UnicapsException):\n    \"\"\"\n    Bad proxy\n    \"\"\"\n\n\nclass AccessDeniedError(ServiceError):\n    \"\"\"\n    Wrong API key\n    IP banned\n    IP not allowed\n    \"\"\"\n\n\nclass LowBalanceError(ServiceError):\n    \"\"\"\n    Low balance\n    \"\"\"\n\n\nclass ServiceTooBusy(ServiceError):\n    \"\"\"\n    No available slots\n    \"\"\"\n\n\nclass SolutionWaitTimeout(ServiceError):\n    \"\"\"\n    Didn't receive solution within N minutes\n    \"\"\"\n\n\nclass TooManyRequestsError(ServiceError):\n    \"\"\"\n    Exceeded request limit\n    \"\"\"\n\n\nclass MalformedRequestError(ServiceError):\n    \"\"\"\n    Exceeded request limit\n    \"\"\"\n\n\nclass BadInputDataError(CaptchaError):\n    \"\"\"\n    Not supported image file\n    Empty file\n    Image file is too big\n    Bad captcha data (eg, wrong googlekey, bad page URL, etc.)\n    \"\"\"\n\n\nclass UnableToSolveError(CaptchaError):\n    \"\"\"\n    Captcha unsolvable\n    \"\"\"\n"
  },
  {
    "path": "unicaps/proxy.py",
    "content": "# -*- coding: UTF-8 -*-\n\"\"\"\nProxy\n\"\"\"\n\n# pylint: disable=unused-import,import-error\nfrom ._misc.proxy import ProxyServer, ProxyServerType\n\n__all__ = 'ProxyServer', 'ProxyServerType'\n"
  }
]