├── .coveragerc ├── .gitconfig ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── custom.md │ └── feature_request.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── install.yml │ ├── lint.yml │ ├── sphinx.yml │ └── test.yml ├── .gitignore ├── .python-version ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── docs ├── Makefile ├── _static │ ├── RuCaptchaMedium.png │ ├── capsolver.jpg │ └── favicon.png ├── conf.py ├── index.rst ├── make.bat ├── modules │ ├── amazon-waf │ │ └── example.rst │ ├── atb-captcha │ │ └── example.rst │ ├── audio │ │ └── example.rst │ ├── bounding-box │ │ └── example.rst │ ├── captcha-fox │ │ └── example.rst │ ├── capy │ │ └── example.rst │ ├── contacts │ │ └── info.md │ ├── control │ │ └── example.rst │ ├── coordinates │ │ └── example.rst │ ├── cut-captcha │ │ └── example.rst │ ├── cyber-siara-captcha │ │ └── example.rst │ ├── datadome-captcha │ │ └── example.rst │ ├── draw-around │ │ └── example.rst │ ├── enum │ │ └── info.rst │ ├── friendly-captcha │ │ └── example.rst │ ├── fun-captcha │ │ └── example.rst │ ├── gee-test │ │ └── example.rst │ ├── grid │ │ └── example.rst │ ├── h-captcha │ │ └── example.rst │ ├── image │ │ └── example.rst │ ├── key │ │ └── example.rst │ ├── lemin │ │ └── example.rst │ ├── license │ │ └── info.md │ ├── main │ │ └── info.md │ ├── mt-captcha │ │ └── example.rst │ ├── other-captcha-services │ │ └── info.md │ ├── other-libs │ │ └── info.md │ ├── prosopo │ │ └── example.rst │ ├── re-captcha │ │ └── example.rst │ ├── rotate │ │ └── example.rst │ ├── serializer │ │ └── info.rst │ ├── tencent │ │ └── example.rst │ ├── text │ │ └── example.rst │ └── turnstile │ │ └── example.rst └── requirements.txt ├── files ├── RuCaptchaHigh.png ├── capsolver.jpg ├── drawing.svg └── img.png ├── pyproject.toml ├── requirements.style.txt ├── requirements.test.txt ├── src ├── __init__.py ├── examples │ ├── 088636.jpg │ ├── 088636.png │ ├── bounding_box_start.jpg │ ├── bounding_box_start.png │ ├── grid.png │ ├── mediacaptcha_audio │ │ ├── recaptcha_55914.mp3 │ │ ├── solvemedia_pakinaback.mp3 │ │ ├── solvemedia_pzalcbniuf.mp3 │ │ └── solvemedia_runphrough.mp3 │ └── rotate │ │ └── rotate_ex.png └── python_rucaptcha │ ├── __init__.py │ ├── __version__.py │ ├── amazon_waf.py │ ├── atb_captcha.py │ ├── audio_captcha.py │ ├── bounding_box_captcha.py │ ├── captcha_fox.py │ ├── capy_puzzle.py │ ├── control.py │ ├── coordinates_captcha.py │ ├── core │ ├── __init__.py │ ├── base.py │ ├── config.py │ ├── enums.py │ ├── result_handler.py │ └── serializer.py │ ├── cutcaptcha.py │ ├── cyber_siara_captcha.py │ ├── datadome_captcha.py │ ├── draw_around_captcha.py │ ├── friendly_captcha.py │ ├── fun_captcha.py │ ├── gee_test.py │ ├── grid_captcha.py │ ├── hcaptcha.py │ ├── image_captcha.py │ ├── key_captcha.py │ ├── lemin_captcha.py │ ├── mt_captcha.py │ ├── prosopo.py │ ├── re_captcha.py │ ├── rotate_captcha.py │ ├── tencent.py │ ├── text_captcha.py │ └── turnstile.py └── tests ├── __init__.py ├── conftest.py ├── test_amazon.py ├── test_audio.py ├── test_bounding_box.py ├── test_capypuzzle.py ├── test_control.py ├── test_coordinates.py ├── test_core.py ├── test_cutcaptcha.py ├── test_cybersiara.py ├── test_datadome.py ├── test_draw_around.py ├── test_friendly_captcha.py ├── test_funcaptcha.py ├── test_geetest.py ├── test_grid.py ├── test_hcaptcha.py ├── test_image.py ├── test_key_captcha.py ├── test_lemin.py ├── test_mtcaptcha.py ├── test_recaptcha.py ├── test_rotate.py ├── test_tencent.py ├── test_text.py └── test_turnstile.py /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | omit = 3 | *__init__* 4 | */tests/* 5 | 6 | include = 7 | */python_rucaptcha/* 8 | 9 | 10 | [report] 11 | omit = 12 | *__init__* 13 | */tests/* 14 | 15 | include = 16 | */python_rucaptcha/* 17 | -------------------------------------------------------------------------------- /.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | name = Andrei Drang 3 | email = python-captcha@pm.me 4 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Library version** 27 | Version of library. 28 | 29 | **Desktop (please complete the following information):** 30 | - OS: [e.g. iOS] 31 | - Browser [e.g. chrome, safari] 32 | - Version [e.g. 22] 33 | 34 | **Additional context** 35 | Add any other context about the problem here. 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "pip" # See documentation for possible values 4 | directory: "/" # Location of package manifests 5 | schedule: 6 | interval: "weekly" 7 | 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | commit-message: 13 | prefix: "[github-actions] " 14 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: [ "master", "release"] 6 | paths: 7 | - '.github/workflows/build.yml' 8 | - 'src/**' 9 | - 'Makefile' 10 | pull_request: 11 | branches: [ "master", "release"] 12 | paths: 13 | - '.github/workflows/build.yml' 14 | - 'src/**' 15 | - 'Makefile' 16 | 17 | jobs: 18 | test_build: 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | python-version: ["3.9", "3.10", "3.11", "3.12"] 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Set up Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: Local build checking 33 | run: make build 34 | -------------------------------------------------------------------------------- /.github/workflows/install.yml: -------------------------------------------------------------------------------- 1 | name: Installation 2 | 3 | on: 4 | push: 5 | branches: [ "master", "release"] 6 | paths: 7 | - '.github/workflows/install.yml' 8 | - 'src/**' 9 | - 'Makefile' 10 | pull_request: 11 | branches: [ "master", "release"] 12 | paths: 13 | - '.github/workflows/install.yml' 14 | - 'src/**' 15 | - 'Makefile' 16 | 17 | jobs: 18 | install: 19 | runs-on: ubuntu-latest 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | python-version: ["3.9", "3.10", "3.11", "3.12"] 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Set up Python ${{ matrix.python-version }} 28 | uses: actions/setup-python@v5 29 | with: 30 | python-version: ${{ matrix.python-version }} 31 | 32 | - name: Local install checking 33 | run: make install 34 | 35 | - name: PYPI install checking 36 | run: pip install python-rucaptcha 37 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | 3 | on: 4 | push: 5 | branches: [ "master", "release"] 6 | paths: 7 | - '.github/workflows/lint.yml' 8 | - 'src/**' 9 | - 'Makefile' 10 | - 'requirements.style.txt' 11 | pull_request: 12 | branches: [ "master", "release"] 13 | paths: 14 | - '.github/workflows/lint.yml' 15 | - 'src/**' 16 | - 'Makefile' 17 | - 'requirements.style.txt' 18 | 19 | jobs: 20 | lint: 21 | runs-on: ubuntu-latest 22 | env: 23 | API_KEY: ${{ secrets.API_KEY }} 24 | strategy: 25 | fail-fast: false 26 | matrix: 27 | python-version: ["3.12"] 28 | 29 | steps: 30 | - uses: actions/checkout@v4 31 | - name: Set up Python ${{ matrix.python-version }} 32 | uses: actions/setup-python@v5 33 | with: 34 | python-version: ${{ matrix.python-version }} 35 | 36 | - name: Install dependencies 37 | run: | 38 | pip install --upgrade pip 39 | pip install -r requirements.style.txt 40 | 41 | - name: Lint 42 | run: make lint 43 | -------------------------------------------------------------------------------- /.github/workflows/sphinx.yml: -------------------------------------------------------------------------------- 1 | name: Sphinx docs 2 | 3 | on: 4 | push: 5 | branches: [ "release" ] 6 | paths: 7 | - '.github/workflows/sphinx.yml' 8 | - 'src/**' 9 | - 'docs/**' 10 | - 'files/**' 11 | 12 | jobs: 13 | docs: 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-python@v5 19 | 20 | - name: Build docs requirements 21 | run: pip install -r docs/requirements.txt 22 | 23 | - name: Build docs 24 | run: make doc 25 | 26 | - name: Deploy 27 | uses: peaceiris/actions-gh-pages@v4 28 | with: 29 | publish_branch: gh-pages 30 | github_token: ${{ secrets.GITHUB_TOKEN }} 31 | publish_dir: docs/_build/html/ 32 | force_orphan: true 33 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ "master", "release"] 6 | paths: 7 | - '.github/workflows/test.yml' 8 | - 'src/**' 9 | - 'tests/**' 10 | - 'Makefile' 11 | - 'requirements.test.txt' 12 | pull_request: 13 | branches: [ "master", "release"] 14 | paths: 15 | - '.github/workflows/test.yml' 16 | - 'src/**' 17 | - 'tests/**' 18 | - 'Makefile' 19 | - 'requirements.test.txt' 20 | schedule: 21 | - cron: "5 0 * * 1" 22 | 23 | jobs: 24 | test: 25 | runs-on: ubuntu-latest 26 | env: 27 | RUCAPTCHA_KEY: ${{ secrets.RUCAPTCHA_KEY }} 28 | DEATHBYCAPTCHA_KEY: ${{ secrets.DEATHBYCAPTCHA_KEY }} 29 | CC_TEST_REPORTER_ID: ${{ secrets.CODE_CLIMATE_COVERAGE_ID }} 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | python-version: ["3.11"] 34 | 35 | steps: 36 | - uses: actions/checkout@v4 37 | - name: Set up Python ${{ matrix.python-version }} 38 | uses: actions/setup-python@v5 39 | with: 40 | python-version: ${{ matrix.python-version }} 41 | 42 | - name: Install dependencies 43 | run: | 44 | pip install --upgrade pip 45 | pip install -r requirements.test.txt 46 | 47 | - name: Test 48 | run: make tests 49 | 50 | - name: Upload coverage to Codecov 51 | uses: codecov/codecov-action@v5 52 | with: 53 | token: ${{ secrets.CODECOV_TOKEN }} 54 | files: ${{github.workspace}}/coverage/coverage.xml 55 | fail_ci_if_error: true 56 | verbose: true 57 | 58 | - name: Upload coverage to Code Climate 59 | uses: paambaati/codeclimate-action@v9.0.0 60 | with: 61 | coverageLocations: ${{github.workspace}}/coverage/coverage.xml:coverage.py 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | python_rucaptcha.egg-info/ 3 | \.idea/ 4 | Web-Interface/ 5 | /ico.png 6 | src/python_rucaptcha/__pycache__/ 7 | build/ 8 | 9 | \.vscode/ 10 | 11 | src/examples/callback_examples/__pycache__/ 12 | 13 | CaptchaTester/callback_examples/__pycache__/ 14 | 15 | __pycache__/ 16 | 17 | dist/ 18 | 19 | env/ 20 | /.coverage 21 | /src/coverage/ 22 | /src/.coverage 23 | /src/coverage/lcov.info 24 | /docs/_build/ 25 | /coverage/ 26 | -------------------------------------------------------------------------------- /.python-version: -------------------------------------------------------------------------------- 1 | 3.11.9 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at drang.andray@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute? 2 | 3 | 1. Make a fork of the project 4 | 1. Make changes 5 | 1. Make a pull-request in the main branch of the project 6 | 1. The pull-request should be followed by a description of the improvements/applications. 7 | 8 | Suggestions for improvements/additions of features and functionality for other similar services are accepted. 9 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## A brief description of the problem. 2 | 3 | 1. Library version 4 | 1. Operating system 5 | 1. Full description of the problem 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Andrei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md LICENSE -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | pip3 install -e . 3 | 4 | remove: 5 | pip3 uninstall python_rucaptcha -y 6 | 7 | refactor: 8 | black docs/ 9 | isort docs/ 10 | 11 | autoflake --in-place \ 12 | --recursive \ 13 | --remove-unused-variables \ 14 | --remove-duplicate-keys \ 15 | --remove-all-unused-imports \ 16 | --ignore-init-module-imports \ 17 | src/ tests/ && \ 18 | black src/ tests/ && \ 19 | isort src/ tests/ 20 | 21 | lint: 22 | autoflake --in-place --recursive src/ --check && \ 23 | black src/ --check && \ 24 | isort src/ --check-only 25 | 26 | build: 27 | pip3 install --upgrade build setuptools 28 | python3 -m build 29 | 30 | upload: 31 | pip3 install twine wheel setuptools build 32 | twine upload dist/* 33 | 34 | tests: install 35 | coverage run --rcfile=.coveragerc -m pytest --verbose --showlocals --pastebin=all \ 36 | tests/ --disable-warnings && \ 37 | coverage report --precision=3 --sort=cover --skip-empty --show-missing && \ 38 | coverage html --precision=3 --skip-empty -d coverage/html/ && \ 39 | coverage xml -o coverage/coverage.xml 40 | 41 | doc: install 42 | cd docs/ && \ 43 | make html -e 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python-rucaptcha 2 | 3 | [![RuCaptchaHigh.png](https://s.vyjava.xyz/files/2024/12-December/17/45247a56/RuCaptchaHigh.png)](https://vyjava.xyz/dashboard/image/45247a56-3332-48ee-8df8-fc95bcfc52f0) 4 | 5 | ### [Capsolver](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=python-rucaptcha) 6 | 7 | [![capsolver.jpg](https://s.vyjava.xyz/files/2024/12-December/17/54e1db0e/capsolver.jpg)](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=python-rucaptcha) 8 | 9 |
10 | 11 | [![PyPI version](https://badge.fury.io/py/python-rucaptcha.svg)](https://badge.fury.io/py/python-rucaptcha) 12 | [![Python versions](https://img.shields.io/pypi/pyversions/python-rucaptcha.svg?logo=python&logoColor=FBE072)](https://badge.fury.io/py/python-rucaptcha) 13 | [![Downloads](https://static.pepy.tech/badge/python-rucaptcha/month)](https://pepy.tech/project/python-rucaptcha) 14 | [![Static Badge](https://img.shields.io/badge/docs-Sphinx-green?label=Documentation&labelColor=gray)](https://andreidrang.github.io/python-rucaptcha/) 15 | 16 | [![Maintainability](https://api.codeclimate.com/v1/badges/aec93bb04a277cf0dde9/maintainability)](https://codeclimate.com/github/AndreiDrang/python-rucaptcha/maintainability) 17 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/b4087362bd024b088b358b3e10e7a62f)](https://www.codacy.com/gh/AndreiDrang/python-rucaptcha/dashboard?utm_source=github.com&utm_medium=referral&utm_content=AndreiDrang/python-rucaptcha&utm_campaign=Badge_Grade) 18 | [![codecov](https://codecov.io/gh/AndreiDrang/python-rucaptcha/branch/master/graph/badge.svg?token=doybTUCfbD)](https://codecov.io/gh/AndreiDrang/python-rucaptcha) 19 | 20 | [![Sphinx docs](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/sphinx.yml/badge.svg?branch=release)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/sphinx.yml) 21 | [![Build](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/build.yml) 22 | [![Installation](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/install.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/install.yml) 23 | [![Tests](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/test.yml) 24 | [![Lint](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/lint.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/lint.yml) 25 | 26 | Python3 library for [RuCaptcha](https://rucaptcha.com/?from=4170435) and [2Captcha](https://2captcha.com/?from=4170435) service API. 27 | 28 | Tested on UNIX based OS. 29 | 30 | The library is intended for software developers and is used to work with the [RuCaptcha](https://rucaptcha.com/?from=4170435) and [2Captcha](https://2captcha.com/?from=4170435) service API. 31 | 32 | Support of the service [Death By Captcha](https://deathbycaptcha.com?refid=1237267242) is integrated into this library, more information in the library documentation or in the [service docs](https://deathbycaptcha.com/api/2captcha?refid=1237267242). 33 | 34 | Application in [RuCaptcha software](https://rucaptcha.com/software/python-rucaptcha) and [2Captcha software](https://2captcha.com/software/python-rucaptcha). 35 | 36 | ## How to install? 37 | 38 | ### pip 39 | 40 | ```bash 41 | pip install python-rucaptcha 42 | ``` 43 | 44 | 45 | ## How to use? 46 | 47 | Is described in the [documentation-website](https://andreidrang.github.io/python-rucaptcha/). 48 | 49 | ## How to test? 50 | 51 | 1. You need set ``RUCAPTCHA_KEY`` in your environment(get this value from you account). 52 | 2. Run command ``make tests``, from root directory. 53 | 54 | 55 | ### Changelog 56 | 57 | For full changelog info check - [Releases page](https://github.com/AndreiDrang/python-rucaptcha/releases). 58 | 59 | - v.6.0 - Library refactoring. Stop using `pydantic`, start using `msgspec`. Move to API v2. Drop Python 3.8 support. More details at [Releases page](https://github.com/AndreiDrang/python-rucaptcha/releases). 60 | - v.5.3 - Added support for [Death By Captcha](https://www.deathbycaptcha.com?refid=1237267242) and other services by changing `service_type` and `url_request` \ `url_response` parameters. 61 | - v.5.2 - Added Audio captcha method. 62 | - v.5.1 - Check [releases page](https://github.com/AndreiDrang/python-rucaptcha/releases). 63 | - v.5.0 - Added AmazonWAF captcha method. 64 | - v.4.2 - Added [Yandex Smart Captcha](https://rucaptcha.com/api-rucaptcha#yandex). 65 | 66 | ### Get API Key to work with the library 67 | 1. On the page - https://rucaptcha.com/enterpage 68 | 2. Find it: [![img.png](https://s.vyjava.xyz/files/2024/12-December/17/ac679557/img.png)](https://vyjava.xyz/dashboard/image/ac679557-f3cc-402f-bf95-6c45d252a2ef) 69 | 70 | ### Contacts 71 | 72 | If you have any questions, please send a message to the [Telegram](https://t.me/pythoncaptcha) chat room. 73 | 74 | Or email python-captcha@pm.me 75 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/_static/RuCaptchaMedium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/docs/_static/RuCaptchaMedium.png -------------------------------------------------------------------------------- /docs/_static/capsolver.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/docs/_static/capsolver.jpg -------------------------------------------------------------------------------- /docs/_static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/docs/_static/favicon.png -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -- Path setup -------------------------------------------------------------- 2 | from datetime import date 3 | 4 | from pallets_sphinx_themes import ProjectLink 5 | 6 | from python_rucaptcha import ( 7 | core, 8 | control, 9 | prosopo, 10 | tencent, 11 | gee_test, 12 | hcaptcha, 13 | turnstile, 14 | amazon_waf, 15 | mt_captcha, 16 | re_captcha, 17 | atb_captcha, 18 | captcha_fox, 19 | capy_puzzle, 20 | fun_captcha, 21 | key_captcha, 22 | grid_captcha, 23 | text_captcha, 24 | image_captcha, 25 | lemin_captcha, 26 | rotate_captcha, 27 | datadome_captcha, 28 | friendly_captcha, 29 | cyber_siara_captcha, 30 | draw_around_captcha, 31 | bounding_box_captcha, 32 | ) 33 | from python_rucaptcha.__version__ import __version__ 34 | 35 | # -- Project information ----------------------------------------------------- 36 | project = "python-rucaptcha" 37 | copyright = ( 38 | f"{date.today().year}, " + f"AndreiDrang; Release - {__version__}; " + f"Last update - {date.today()}" 39 | ) 40 | author = "AndreiDrang" 41 | 42 | # -- General configuration --------------------------------------------------- 43 | extensions = ( 44 | "myst_parser", 45 | "sphinx.ext.napoleon", 46 | "pallets_sphinx_themes", 47 | "enum_tools.autoenum", 48 | "notfound.extension", 49 | ) 50 | myst_enable_extensions = ["deflist"] 51 | intersphinx_mapping = {"python": ("https://docs.python.org/3.10/", None)} 52 | templates_path = ["_templates"] 53 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 54 | 55 | # -- Options for HTML output ------------------------------------------------- 56 | # Theme config 57 | html_theme = "jinja" 58 | html_theme_options = {"index_sidebar_logo": False} 59 | html_static_path = ["_static"] 60 | html_favicon = "_static/favicon.png" 61 | html_logo = "_static/RuCaptchaMedium.png" 62 | html_title = f"python-rucaptcha ({__version__})" 63 | html_show_sourcelink = False 64 | 65 | html_context = { 66 | "project_links": [ 67 | ProjectLink("PyPI Releases", "https://pypi.org/project/python-rucaptcha/"), 68 | ProjectLink("Source Code", "https://github.com/AndreiDrang/python-rucaptcha"), 69 | ProjectLink( 70 | "2Captcha/RuCaptcha", 71 | "https://rucaptcha.com/?from=4170435", 72 | ), 73 | ProjectLink( 74 | "DeathByCaptcha", 75 | "https://deathbycaptcha.com?refid=1237267242", 76 | ), 77 | ProjectLink("RedPandaDev group", "https://red-panda-dev.xyz/blog/"), 78 | ] 79 | } 80 | html_sidebars = { 81 | "index": ["project.html", "localtoc.html", "searchbox.html", "ethicalads.html"], 82 | "**": ["localtoc.html", "relations.html", "searchbox.html", "ethicalads.html"], 83 | } 84 | 85 | # Typehints config 86 | autodoc_typehints = "both" 87 | autodoc_typehints_description_target = "documented" 88 | autodoc_typehints_format = "short" 89 | 90 | # Napoleon settings 91 | napoleon_google_docstring = True 92 | napoleon_numpy_docstring = False 93 | napoleon_include_init_with_doc = True 94 | napoleon_include_private_with_doc = False 95 | napoleon_include_special_with_doc = False 96 | napoleon_use_admonition_for_examples = True 97 | napoleon_use_admonition_for_notes = True 98 | napoleon_use_admonition_for_references = True 99 | napoleon_use_ivar = True 100 | napoleon_use_param = True 101 | napoleon_use_rtype = True 102 | napoleon_preprocess_types = True 103 | napoleon_type_aliases = True 104 | napoleon_attr_annotations = True 105 | 106 | autodoc_preserve_defaults = False 107 | autodoc_member_order = "bysource" 108 | autodoc_class_signature = "mixed" 109 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. rst-class:: hide-header 2 | 3 | python-rucaptcha 4 | ================ 5 | .. image:: _static/RuCaptchaMedium.png 6 | :align: center 7 | 8 | Python3 library for `RuCaptcha `_ and `2Captcha `_ service API. 9 | 10 | The library is intended for software developers and is used to work with the `RuCaptcha `_ and `2Captcha `_ service API. 11 | 12 | Support of the service `Death By Captcha `_ is integrated into this library, more information in the library documentation or in the `service docs `_. 13 | 14 | Check our other projects here - `RedPandaDev group `_. 15 | 16 | .. toctree:: 17 | :maxdepth: 1 18 | :caption: Start here: 19 | 20 | modules/main/info.md 21 | modules/other-libs/info.md 22 | modules/license/info.md 23 | modules/contacts/info.md 24 | modules/other-captcha-services/info.md 25 | 26 | .. toctree:: 27 | :maxdepth: 3 28 | :caption: Captcha examples: 29 | 30 | modules/h-captcha/example.rst 31 | modules/fun-captcha/example.rst 32 | modules/gee-test/example.rst 33 | modules/capy/example.rst 34 | modules/amazon-waf/example.rst 35 | modules/key/example.rst 36 | modules/lemin/example.rst 37 | modules/re-captcha/example.rst 38 | modules/rotate/example.rst 39 | modules/text/example.rst 40 | modules/turnstile/example.rst 41 | modules/image/example.rst 42 | modules/audio/example.rst 43 | modules/cut-captcha/example.rst 44 | modules/datadome-captcha/example.rst 45 | modules/cyber-siara-captcha/example.rst 46 | modules/grid/example.rst 47 | modules/draw-around/example.rst 48 | modules/bounding-box/example.rst 49 | modules/coordinates/example.rst 50 | modules/mt-captcha/example.rst 51 | modules/friendly-captcha/example.rst 52 | modules/tencent/example.rst 53 | modules/prosopo/example.rst 54 | modules/atb-captcha/example.rst 55 | modules/captcha-fox/example.rst 56 | modules/control/example.rst 57 | 58 | .. toctree:: 59 | :maxdepth: 2 60 | :caption: Additional modules 61 | 62 | modules/enum/info.rst 63 | modules/serializer/info.rst 64 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | 2 | @ECHO OFF 3 | 4 | pushd %~dp0 5 | 6 | REM Command file for Sphinx documentation 7 | 8 | if "%SPHINXBUILD%" == "" ( 9 | set SPHINXBUILD=sphinx-build 10 | ) 11 | set SOURCEDIR=. 12 | set BUILDDIR=_build 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 20 | echo.installed, then set the SPHINXBUILD environment variable to point 21 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 22 | echo.may add the Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd -------------------------------------------------------------------------------- /docs/modules/amazon-waf/example.rst: -------------------------------------------------------------------------------- 1 | AmazonWAF 2 | ========= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.amazon_waf import AmazonWAF 9 | 10 | 11 | .. autoclass:: python_rucaptcha.amazon_waf.AmazonWAF 12 | :members: -------------------------------------------------------------------------------- /docs/modules/atb-captcha/example.rst: -------------------------------------------------------------------------------- 1 | atbCaptcha 2 | ========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.atb_captcha import atbCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.atb_captcha.atbCaptcha 12 | :members: 13 | -------------------------------------------------------------------------------- /docs/modules/audio/example.rst: -------------------------------------------------------------------------------- 1 | AudioCaptcha 2 | ============ 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.audio_captcha import AudioCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.audio_captcha.AudioCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/bounding-box/example.rst: -------------------------------------------------------------------------------- 1 | BoundingBoxCaptcha 2 | ================== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.bounding_box_captcha import BoundingBoxCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.bounding_box_captcha.BoundingBoxCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/captcha-fox/example.rst: -------------------------------------------------------------------------------- 1 | CaptchaFox 2 | ========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.captcha_fox import CaptchaFox 9 | 10 | 11 | .. autoclass:: python_rucaptcha.captcha_fox.CaptchaFox 12 | :members: -------------------------------------------------------------------------------- /docs/modules/capy/example.rst: -------------------------------------------------------------------------------- 1 | CapyPuzzle 2 | ========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.capy_puzzle import CapyPuzzle 9 | 10 | 11 | .. autoclass:: python_rucaptcha.capy_puzzle.CapyPuzzle 12 | :members: -------------------------------------------------------------------------------- /docs/modules/contacts/info.md: -------------------------------------------------------------------------------- 1 | # Contacts 2 | 3 | If you have any questions, please send a message to the [Telegram](https://t.me/pythoncaptcha) chat room. 4 | 5 | Or email: [python-captcha@pm.me](mailto:python-captcha@pm.me) -------------------------------------------------------------------------------- /docs/modules/control/example.rst: -------------------------------------------------------------------------------- 1 | Control 2 | ======= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.control import Control 9 | 10 | 11 | .. autoclass:: python_rucaptcha.control.Control 12 | :members: -------------------------------------------------------------------------------- /docs/modules/coordinates/example.rst: -------------------------------------------------------------------------------- 1 | CoordinatesCaptcha 2 | ================== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.coordinates_captcha import CoordinatesCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.coordinates_captcha.CoordinatesCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/cut-captcha/example.rst: -------------------------------------------------------------------------------- 1 | CutCaptcha 2 | ========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.cutcaptcha import CutCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.cutcaptcha.CutCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/cyber-siara-captcha/example.rst: -------------------------------------------------------------------------------- 1 | CyberSiARA 2 | ========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.cyber_siara_captcha import CyberSiARACaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.cyber_siara_captcha.CyberSiARACaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/datadome-captcha/example.rst: -------------------------------------------------------------------------------- 1 | DataDome 2 | ======== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.datadome_captcha import DataDomeCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.datadome_captcha.DataDomeCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/draw-around/example.rst: -------------------------------------------------------------------------------- 1 | DrawAroundCaptcha 2 | ================= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.draw_around_captcha import DrawAroundCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.draw_around_captcha.DrawAroundCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/enum/info.rst: -------------------------------------------------------------------------------- 1 | Enum 2 | ==== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.core import enums 9 | 10 | 11 | .. autoenum:: python_rucaptcha.core.enums.MyEnum 12 | :members: 13 | 14 | .. autoenum:: python_rucaptcha.core.enums.ServiceEnm 15 | :members: 16 | 17 | .. autoenum:: python_rucaptcha.core.enums.SaveFormatsEnm 18 | :members: 19 | 20 | .. autoenum:: python_rucaptcha.core.enums.GeetestEnm 21 | :members: 22 | 23 | .. autoenum:: python_rucaptcha.core.enums.ImageCaptchaEnm 24 | :members: 25 | 26 | .. autoenum:: python_rucaptcha.core.enums.CapyPuzzleEnm 27 | :members: 28 | 29 | .. autoenum:: python_rucaptcha.core.enums.FunCaptchaEnm 30 | :members: 31 | 32 | .. autoenum:: python_rucaptcha.core.enums.ReCaptchaEnm 33 | :members: 34 | 35 | .. autoenum:: python_rucaptcha.core.enums.LeminCaptchaEnm 36 | :members: 37 | 38 | .. autoenum:: python_rucaptcha.core.enums.HCaptchaEnm 39 | :members: 40 | 41 | .. autoenum:: python_rucaptcha.core.enums.KeyCaptchaEnm 42 | :members: 43 | 44 | .. autoenum:: python_rucaptcha.core.enums.RotateCaptchaEnm 45 | :members: 46 | 47 | .. autoenum:: python_rucaptcha.core.enums.ControlEnm 48 | :members: 49 | 50 | .. autoenum:: python_rucaptcha.core.enums.TurnstileCaptchaEnm 51 | :members: 52 | 53 | .. autoenum:: python_rucaptcha.core.enums.AmazonWAFCaptchaEnm 54 | :members: 55 | 56 | .. autoenum:: python_rucaptcha.core.enums.CutCaptchaEnm 57 | :members: 58 | 59 | .. autoenum:: python_rucaptcha.core.enums.DataDomeSliderEnm 60 | :members: 61 | 62 | .. autoenum:: python_rucaptcha.core.enums.CyberSiARAEnm 63 | :members: 64 | 65 | .. autoenum:: python_rucaptcha.core.enums.MTCaptchaEnm 66 | :members: 67 | 68 | .. autoenum:: python_rucaptcha.core.enums.BoundingBoxCaptchaEnm 69 | :members: 70 | 71 | .. autoenum:: python_rucaptcha.core.enums.DrawAroundCaptchaEnm 72 | :members: 73 | 74 | .. autoenum:: python_rucaptcha.core.enums.CoordinatesCaptchaEnm 75 | :members: 76 | 77 | .. autoenum:: python_rucaptcha.core.enums.GridCaptchaEnm 78 | :members: 79 | 80 | .. autoenum:: python_rucaptcha.core.enums.FriendlyCaptchaEnm 81 | :members: 82 | 83 | .. autoenum:: python_rucaptcha.core.enums.TencentEnm 84 | :members: 85 | 86 | .. autoenum:: python_rucaptcha.core.enums.ProsopoEnm 87 | :members: 88 | 89 | .. autoenum:: python_rucaptcha.core.enums.atbCaptchaEnm 90 | :members: 91 | -------------------------------------------------------------------------------- /docs/modules/friendly-captcha/example.rst: -------------------------------------------------------------------------------- 1 | FriendlyCaptcha 2 | =============== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.friendly_captcha import FriendlyCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.friendly_captcha.FriendlyCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/fun-captcha/example.rst: -------------------------------------------------------------------------------- 1 | FunCaptcha 2 | ========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.fun_captcha import FunCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.fun_captcha.FunCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/gee-test/example.rst: -------------------------------------------------------------------------------- 1 | GeeTest 2 | ======= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.gee_test import GeeTest 9 | 10 | 11 | .. autoclass:: python_rucaptcha.gee_test.GeeTest 12 | :members: -------------------------------------------------------------------------------- /docs/modules/grid/example.rst: -------------------------------------------------------------------------------- 1 | GridCaptcha 2 | =========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.grid_captcha import GridCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.grid_captcha.GridCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/h-captcha/example.rst: -------------------------------------------------------------------------------- 1 | HCaptcha 2 | ======== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.hcaptcha import HCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.hcaptcha.HCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/image/example.rst: -------------------------------------------------------------------------------- 1 | ImageCaptcha 2 | ============ 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.image_captcha import ImageCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.image_captcha.ImageCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/key/example.rst: -------------------------------------------------------------------------------- 1 | KeyCaptcha 2 | =========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.key_captcha import KeyCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.key_captcha.KeyCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/lemin/example.rst: -------------------------------------------------------------------------------- 1 | LeminCaptcha 2 | ============ 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.lemin_captcha import LeminCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.lemin_captcha.LeminCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/license/info.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | MIT License 4 | 5 | Copyright 2022 Andrei 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /docs/modules/main/info.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | 4 | ![](../../_static/RuCaptchaMedium.png) 5 | 6 | ### [Capsolver](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=python-rucaptcha) 7 | 8 | [![Capsolver](../../_static/capsolver.jpg)](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=python-rucaptcha) 9 | 10 |
11 | 12 | [![PyPI version](https://badge.fury.io/py/python-rucaptcha.svg)](https://badge.fury.io/py/python-rucaptcha) 13 | [![Python versions](https://img.shields.io/pypi/pyversions/python-rucaptcha.svg?logo=python&logoColor=FBE072)](https://badge.fury.io/py/python-rucaptcha) 14 | [![Downloads](https://pepy.tech/badge/python-rucaptcha/month)](https://pepy.tech/project/python-rucaptcha) 15 | [![Static Badge](https://img.shields.io/badge/docs-Sphinx-green?label=Documentation&labelColor=gray)](https://andreidrang.github.io/python-rucaptcha/) 16 | 17 | [![Maintainability](https://api.codeclimate.com/v1/badges/aec93bb04a277cf0dde9/maintainability)](https://codeclimate.com/github/AndreiDrang/python-rucaptcha/maintainability) 18 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/b4087362bd024b088b358b3e10e7a62f)](https://www.codacy.com/gh/AndreiDrang/python-rucaptcha/dashboard?utm_source=github.com&utm_medium=referral&utm_content=AndreiDrang/python-rucaptcha&utm_campaign=Badge_Grade) 19 | [![codecov](https://codecov.io/gh/AndreiDrang/python-rucaptcha/branch/master/graph/badge.svg?token=doybTUCfbD)](https://codecov.io/gh/AndreiDrang/python-rucaptcha) 20 | 21 | [![Build](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/build.yml) 22 | [![Installation](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/install.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/install.yml) 23 | [![Tests](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/test.yml) 24 | [![Lint](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/lint.yml/badge.svg?branch=master)](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/lint.yml) 25 | 26 | Python3 library for [RuCaptcha](https://rucaptcha.com/?from=4170435) and [2Captcha](https://2captcha.com/?from=4170435) service API. 27 | 28 | Tested on UNIX based OS. 29 | 30 | The library is intended for software developers and is used to work with the [RuCaptcha](https://rucaptcha.com/?from=4170435) and [2Captcha](https://2captcha.com/?from=4170435) service API. 31 | 32 | Support of the service [Death By Captcha](https://deathbycaptcha.com?refid=1237267242) is integrated into this library, more information in the library documentation or in the [service docs](https://deathbycaptcha.com/api/2captcha?refid=1237267242). 33 | 34 | Application in [RuCaptcha software](https://rucaptcha.com/software/python-rucaptcha) and [2Captcha software](https://2captcha.com/software/python-rucaptcha). 35 | 36 | *** 37 | 38 | You can check our other projects here - [RedPandaDev group](https://red-panda-dev.xyz/blog/). 39 | 40 | *** 41 | 42 | 43 | ## How to install? 44 | 45 | ### pip 46 | 47 | ```bash 48 | pip install python-rucaptcha 49 | ``` 50 | 51 | ## How to test? 52 | 53 | 1. You need set ``RUCAPTCHA_KEY`` in your environment(get this value from you account). 54 | 2. You need set ``DEATHBYCAPTCHA_KEY`` in your environment(get this value from you account). 55 | 3. Run command ``make tests``, from root directory. 56 | 57 | 58 | ### Changelog 59 | 60 | Check [releases page](https://github.com/AndreiDrang/python-rucaptcha/releases). 61 | 62 | ### Contacts 63 | 64 | If you have any questions, please send a message to the [Telegram](https://t.me/pythoncaptcha) chat room. 65 | 66 | Or email python-captcha@pm.me 67 | -------------------------------------------------------------------------------- /docs/modules/mt-captcha/example.rst: -------------------------------------------------------------------------------- 1 | MTCaptcha 2 | ========= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.mt_captcha import MTCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.mt_captcha.MTCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/other-captcha-services/info.md: -------------------------------------------------------------------------------- 1 | # DeathByCaptcha and other services 2 | 3 | Using [Death By Captcha](https://deathbycaptcha.com?refid=1237267242) and other services is possible by changing "service_type" and "url_request"\"url_response" parameters. 4 | 5 | More info about [Death By Captcha](https://deathbycaptcha.com?refid=1237267242) integration u can find in [service docs](https://deathbycaptcha.com/api/2captcha?refid=1237267242). 6 | 7 | 8 | ## DeathByCaptcha: 9 | ```python 10 | 11 | from python_rucaptcha.control import Control 12 | from python_rucaptcha.core.enums import ControlEnm, ServiceEnm 13 | 14 | serv_username= 'your.username' 15 | serv_password = 'your.passwordQq11' 16 | deathbycaptcha_api_key = f"{serv_username}:{serv_password}" 17 | 18 | result = Control(rucaptcha_key=deathbycaptcha_api_key, 19 | service_type=ServiceEnm.DEATHBYCAPTCHA, 20 | action=ControlEnm.GETBALANCE.value).additional_methods() 21 | ``` 22 | 23 | ```python 24 | 25 | from python_rucaptcha.control import Control 26 | from python_rucaptcha.core.enums import ControlEnm, ServiceEnm 27 | 28 | serv_username= 'your.username' 29 | serv_password = 'your.passwordQq11' 30 | deathbycaptcha_api_key = f"{serv_username}:{serv_password}" 31 | 32 | result = Control(rucaptcha_key=deathbycaptcha_api_key, 33 | service_type=ServiceEnm.DEATHBYCAPTCHA, 34 | url_request='http://api.deathbycaptcha.com/2captcha/in.php', 35 | url_response='http://api.deathbycaptcha.com/2captcha/res.php', 36 | action=ControlEnm.GETBALANCE.value).additional_methods() 37 | ``` 38 | 39 | ```python 40 | 41 | from python_rucaptcha.re_captcha import ReCaptcha, ReCaptchaEnm 42 | 43 | serv_username= 'your.username' 44 | serv_password = 'your.passwordQq11' 45 | deathbycaptcha_api_key = f"{serv_username}:{serv_password}" 46 | 47 | result = ReCaptcha(rucaptcha_key=deathbycaptcha_api_key, 48 | service_type="deathbycaptcha", 49 | pageurl="https://rucaptcha.com/demo/recaptcha-v2", 50 | googlekey="6LeIxboZAAAAAFQy7d8GPzgRZu2bV0GwKS8ue_cH", 51 | method=ReCaptchaEnm.USER_RECAPTCHA.value 52 | ).captcha_handler() 53 | ``` 54 | 55 | ```python 56 | 57 | from python_rucaptcha.hcaptcha import HCaptcha, HCaptchaEnm 58 | 59 | serv_username= 'your.username' 60 | serv_password = 'your.passwordQq11' 61 | deathbycaptcha_api_key = f"{serv_username}:{serv_password}" 62 | 63 | result = HCaptcha(rucaptcha_key=deathbycaptcha_api_key, 64 | service_type="deathbycaptcha", 65 | sitekey="3ceb8624-1970-4e6b-91d5-70317b70b651", 66 | pageurl="https://rucaptcha.com/demo/hcaptcha", 67 | method=HCaptchaEnm.HCAPTCHA.value 68 | ).captcha_handler() 69 | ``` 70 | 71 | And etc, more info in [service docs](https://deathbycaptcha.com/api/2captcha?refid=1237267242). 72 | 73 | ## Or you can use other service which support RuCaptcha\2Captcha API-like requests 74 | ```python 75 | 76 | from python_rucaptcha.control import Control 77 | from python_rucaptcha.core.enums import ControlEnm 78 | 79 | result = Control(rucaptcha_key="someotherapikey", 80 | service_type='SomeOtherGoodService', 81 | url_request='http://some-good-server.com/in.php', 82 | url_response='http://some-good-server.com/res.php', 83 | action=ControlEnm.GETBALANCE.value).additional_methods() 84 | ``` 85 | -------------------------------------------------------------------------------- /docs/modules/other-libs/info.md: -------------------------------------------------------------------------------- 1 | # Other captcha-solving services libs 2 | 3 | ## Other libraries for Captcha-solving services 4 | 1. [RuCaptcha / 2Captcha](https://github.com/AndreiDrang/python-rucaptcha) 5 | 2. [AntiCaptcha](https://github.com/AndreiDrang/python3-anticaptcha) 6 | 3. [Capsolver](https://github.com/AndreiDrang/python3-captchaai) 7 | 8 | ## Rust 9 | 1. [Rust-AntiCaptcha crate](https://crates.io/crates/rust-anticaptcha) 10 | 11 | Our other projects: 12 | - [RedPandaDev group](https://red-panda-dev.xyz/blog/) 13 | -------------------------------------------------------------------------------- /docs/modules/prosopo/example.rst: -------------------------------------------------------------------------------- 1 | Prosopo 2 | ======= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.prosopo import Prosopo 9 | 10 | 11 | .. autoclass:: python_rucaptcha.prosopo.Prosopo 12 | :members: 13 | -------------------------------------------------------------------------------- /docs/modules/re-captcha/example.rst: -------------------------------------------------------------------------------- 1 | ReCaptcha 2 | ========= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.re_captcha import ReCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.re_captcha.ReCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/rotate/example.rst: -------------------------------------------------------------------------------- 1 | RotateCaptcha 2 | ============= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.rotate_captcha import RotateCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.rotate_captcha.RotateCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/serializer/info.rst: -------------------------------------------------------------------------------- 1 | Serializer 2 | ========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.core import serializer 9 | 10 | 11 | .. autoclass:: python_rucaptcha.core.serializer.GetTaskResultResponseSer 12 | :members: 13 | :undoc-members: 14 | -------------------------------------------------------------------------------- /docs/modules/tencent/example.rst: -------------------------------------------------------------------------------- 1 | Tencent 2 | ======= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.tencent import Tencent 9 | 10 | 11 | .. autoclass:: python_rucaptcha.tencent.Tencent 12 | :members: 13 | -------------------------------------------------------------------------------- /docs/modules/text/example.rst: -------------------------------------------------------------------------------- 1 | TextCaptcha 2 | =========== 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.text_captcha import TextCaptcha 9 | 10 | 11 | .. autoclass:: python_rucaptcha.text_captcha.TextCaptcha 12 | :members: -------------------------------------------------------------------------------- /docs/modules/turnstile/example.rst: -------------------------------------------------------------------------------- 1 | Turnstile 2 | ========= 3 | 4 | To import this module: 5 | 6 | .. code-block:: python 7 | 8 | from python_rucaptcha.turnstile import Turnstile 9 | 10 | 11 | .. autoclass:: python_rucaptcha.turnstile.Turnstile 12 | :members: -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==8.3.0 2 | pallets_sphinx_themes==2.3.0 3 | myst-parser==4.0.1 4 | enum-tools[sphinx]==0.13.0 5 | requests>=2.32.0 # not directly required, pinned by Snyk to avoid a vulnerability 6 | urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability 7 | zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability 8 | -------------------------------------------------------------------------------- /files/RuCaptchaHigh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/files/RuCaptchaHigh.png -------------------------------------------------------------------------------- /files/capsolver.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/files/capsolver.jpg -------------------------------------------------------------------------------- /files/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/files/img.png -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.black] 2 | line-length = 110 3 | target-version = ['py311'] 4 | exclude = ''' 5 | /( 6 | \.git 7 | | \.hg 8 | | \.mypy_cache 9 | | \.tox 10 | | \.venv 11 | | _build 12 | | buck-out 13 | | build 14 | | dist 15 | )/ 16 | ''' 17 | 18 | [tool.isort] 19 | profile = "black" 20 | skip_glob = "examples/**" 21 | line_length = 110 22 | length_sort = true 23 | 24 | [tool.pytest.ini_options] 25 | minversion = "7.0" 26 | asyncio_mode = "auto" 27 | testpaths = [ 28 | "tests", 29 | ] 30 | addopts = "-vv --tb=short --durations=10" 31 | 32 | [build-system] 33 | requires = ["setuptools"] 34 | build-backend = "setuptools.build_meta" 35 | 36 | [project] 37 | name = "python-rucaptcha" 38 | dynamic = ["version"] 39 | authors = [ 40 | {name = "AndreiDrang", email = "python-captcha@pm.me"}, 41 | ] 42 | description = "Python 3.9+ RuCaptcha library with AIO module." 43 | readme = "README.md" 44 | requires-python = ">=3.9" 45 | keywords = [ "captcha", 46 | "rucaptcha", 47 | "2captcha", 48 | "deathbycaptcha", 49 | "recaptcha", 50 | "geetest", 51 | "hcaptcha", 52 | "capypuzzle", 53 | "rotatecaptcha", 54 | "funcaptcha", 55 | "keycaptcha", 56 | "python3", 57 | "recaptcha", 58 | "captcha", 59 | "security", 60 | "tencent", 61 | "atb_captcha", 62 | "python-library", 63 | "python-rucaptcha", 64 | "rucaptcha-client", 65 | "yandex", 66 | "turnstile", 67 | "amazon", 68 | "amazon_waf", 69 | "friendly-captcha" 70 | ] 71 | license = "MIT" 72 | classifiers = [ 73 | "Development Status :: 5 - Production/Stable", 74 | "Programming Language :: Python", 75 | "Programming Language :: Python :: 3", 76 | "Programming Language :: Python :: 3 :: Only", 77 | "Programming Language :: Python :: 3.9", 78 | "Programming Language :: Python :: 3.10", 79 | "Programming Language :: Python :: 3.11", 80 | "Programming Language :: Python :: 3.12", 81 | "Framework :: AsyncIO", 82 | "Operating System :: Unix", 83 | "Operating System :: Microsoft :: Windows", 84 | "Operating System :: MacOS", 85 | ] 86 | dependencies = [ 87 | "requests>=2.21.0", 88 | "aiohttp>=3.9.2", 89 | "msgspec>=0.18,<0.20", 90 | "tenacity>=8,<10" 91 | ] 92 | 93 | [tool.setuptools.packages.find] 94 | where = ["src"] 95 | include = ["python_rucaptcha*"] 96 | 97 | [tool.setuptools.dynamic] 98 | version = {attr = "python_rucaptcha.__version__"} 99 | 100 | [project.urls] 101 | Homepage = "https://andreidrang.github.io/python-rucaptcha/" 102 | Documentation = "https://andreidrang.github.io/python-rucaptcha/" 103 | Repository = "https://github.com/AndreiDrang/python-rucaptcha" 104 | Issues = "https://github.com/AndreiDrang/python-rucaptcha/issues" 105 | Changelog = "https://github.com/AndreiDrang/python-rucaptcha/releases" 106 | -------------------------------------------------------------------------------- /requirements.style.txt: -------------------------------------------------------------------------------- 1 | # codestyle 2 | isort==6.* 3 | black==25.1.0 4 | autoflake==2.* 5 | -------------------------------------------------------------------------------- /requirements.test.txt: -------------------------------------------------------------------------------- 1 | pytest==8.* 2 | coverage==7.* 3 | pytest-asyncio==0.* 4 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/__init__.py -------------------------------------------------------------------------------- /src/examples/088636.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/088636.jpg -------------------------------------------------------------------------------- /src/examples/088636.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/088636.png -------------------------------------------------------------------------------- /src/examples/bounding_box_start.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/bounding_box_start.jpg -------------------------------------------------------------------------------- /src/examples/bounding_box_start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/bounding_box_start.png -------------------------------------------------------------------------------- /src/examples/grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/grid.png -------------------------------------------------------------------------------- /src/examples/mediacaptcha_audio/recaptcha_55914.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/mediacaptcha_audio/recaptcha_55914.mp3 -------------------------------------------------------------------------------- /src/examples/mediacaptcha_audio/solvemedia_pakinaback.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/mediacaptcha_audio/solvemedia_pakinaback.mp3 -------------------------------------------------------------------------------- /src/examples/mediacaptcha_audio/solvemedia_pzalcbniuf.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/mediacaptcha_audio/solvemedia_pzalcbniuf.mp3 -------------------------------------------------------------------------------- /src/examples/mediacaptcha_audio/solvemedia_runphrough.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/mediacaptcha_audio/solvemedia_runphrough.mp3 -------------------------------------------------------------------------------- /src/examples/rotate/rotate_ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/examples/rotate/rotate_ex.png -------------------------------------------------------------------------------- /src/python_rucaptcha/__init__.py: -------------------------------------------------------------------------------- 1 | from python_rucaptcha.__version__ import __version__ # noqa 2 | -------------------------------------------------------------------------------- /src/python_rucaptcha/__version__.py: -------------------------------------------------------------------------------- 1 | __version__ = "6.4.0" 2 | -------------------------------------------------------------------------------- /src/python_rucaptcha/amazon_waf.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import AmazonWAFCaptchaEnm 5 | 6 | 7 | class AmazonWAF(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websiteKey: str, 12 | iv: str, 13 | context: str, 14 | method: Union[str, AmazonWAFCaptchaEnm] = AmazonWAFCaptchaEnm.AmazonTaskProxyless, 15 | *args, 16 | **kwargs, 17 | ): 18 | """ 19 | The class is used to work with Amazon WAF 20 | 21 | Args: 22 | rucaptcha_key: User API key 23 | websiteURL: Full URL of the captcha page 24 | websiteKey: Key value from the page 25 | iv: Value iv from the page 26 | context: Value of context from page 27 | method: Captcha type 28 | 29 | Examples: 30 | >>> AmazonWAF(rucaptcha_key="aa9011f31111181111168611f1151122", 31 | ... websiteURL="https://page-with-waf.com/", 32 | ... websiteKey="some-site-key", 33 | ... iv="some-iv-value", 34 | ... context="some-context-value").captcha_handler() 35 | { 36 | "errorId":0, 37 | "status":"ready", 38 | "solution":{ 39 | "captcha_voucher":"eyJ0eXAiO...oQjTnJlBvAW4", 40 | "existing_token":"f8ab5749-f916-...5D8yAA39JtKVbw=" 41 | }, 42 | "cost":"0.00145", 43 | "ip":"1.2.3.4", 44 | "createTime":1692863536, 45 | "endTime":1692863556, 46 | "solveCount":0, 47 | "taskId": 73243152973, 48 | } 49 | 50 | >>> AmazonWAF(rucaptcha_key="aa9011f31111181111168611f1151122", 51 | ... websiteURL="https://page-with-waf.com/", 52 | ... websiteKey="some-site-key", 53 | ... iv="some-iv-value", 54 | ... context="some-context-value").aio_captcha_handler() 55 | { 56 | "errorId":0, 57 | "status":"ready", 58 | "solution":{ 59 | "captcha_voucher":"eyJ0eXAiO...oQjTnJlBvAW4", 60 | "existing_token":"f8ab5749-f916-...5D8yAA39JtKVbw=" 61 | }, 62 | "cost":"0.00145", 63 | "ip":"1.2.3.4", 64 | "createTime":1692863536, 65 | "endTime":1692863556, 66 | "solveCount":0, 67 | "taskId": 73243152973, 68 | } 69 | 70 | Returns: 71 | Dict with full server response 72 | 73 | Notes: 74 | https://rucaptcha.com/api-docs/amazon-aws-waf-captcha 75 | """ 76 | super().__init__(method=method, *args, **kwargs) 77 | 78 | # check user params 79 | if method not in AmazonWAFCaptchaEnm.list_values(): 80 | raise ValueError(f"Invalid method parameter set, available - {AmazonWAFCaptchaEnm.list_values()}") 81 | # insert `gt` param to payload 82 | self.create_task_payload["task"].update( 83 | { 84 | "websiteURL": websiteURL, 85 | "websiteKey": websiteKey, 86 | "iv": iv, 87 | "context": context, 88 | } 89 | ) 90 | 91 | def captcha_handler(self, **kwargs) -> dict: 92 | """ 93 | Synchronous method for captcha solving 94 | 95 | Returns: 96 | Dict with full server response 97 | 98 | Notes: 99 | Check class docstirng for more info 100 | """ 101 | 102 | return self._processing_response(**kwargs) 103 | 104 | async def aio_captcha_handler(self) -> dict: 105 | """ 106 | Asynchronous method for captcha solving 107 | 108 | Returns: 109 | Dict with full server response 110 | 111 | Notes: 112 | Check class docstirng for more info 113 | """ 114 | return await self._aio_processing_response() 115 | -------------------------------------------------------------------------------- /src/python_rucaptcha/atb_captcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import atbCaptchaEnm 5 | 6 | 7 | class atbCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | appId: str, 12 | apiServer: str, 13 | method: Union[str, atbCaptchaEnm] = atbCaptchaEnm.AtbCaptchaTaskProxyless, 14 | *args, 15 | **kwargs, 16 | ): 17 | """ 18 | The class is used to work with CapyPuzzle. 19 | 20 | Args: 21 | rucaptcha_key: User API key 22 | websiteURL: The full URL of target web page where the captcha is loaded. 23 | We do not open the page, not a problem if it is available only for authenticated users 24 | appId: The value of `appId` parameter in the website source code. 25 | apiServer: The value of `apiServer` parameter in the website source code. 26 | method: Captcha type 27 | 28 | Examples: 29 | >>> atbCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 30 | ... websiteURL="https://www.tencentcloud.com/account/register", 31 | ... appId="2009899766", 32 | ... apiServer="https://cap.aisecurius.com", 33 | ... method=atbCaptchaEnm.AtbCaptchaTaskProxyless.value, 34 | ... ).captcha_handler() 35 | { 36 | "errorId":0, 37 | "status":"ready", 38 | "solution":{ 39 | "token": "sl191suxzluwxxh6f:" 40 | }, 41 | "cost":"0.00299", 42 | "ip":"1.2.3.4", 43 | "createTime":1692863536, 44 | "endTime":1692863556, 45 | "solveCount":1, 46 | "taskId":75190409731 47 | } 48 | 49 | >>> await atbCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 50 | ... websiteURL="https://www.tencentcloud.com/account/register", 51 | ... appId="2009899766", 52 | ... apiServer="https://cap.aisecurius.com", 53 | ... method=atbCaptchaEnm.AtbCaptchaTaskProxyless.value, 54 | ... ).aio_captcha_handler() 55 | { 56 | "errorId":0, 57 | "status":"ready", 58 | "solution":{ 59 | "token": "sl191suxzluwxxh6f:" 60 | }, 61 | "cost":"0.00299", 62 | "ip":"1.2.3.4", 63 | "createTime":1692863536, 64 | "endTime":1692863556, 65 | "solveCount":1, 66 | "taskId":75190409731 67 | } 68 | 69 | Returns: 70 | Dict with full server response 71 | 72 | Notes: 73 | https://rucaptcha.com/api-docs/atb-captcha 74 | 75 | https://2captcha.com/api-docs/atb-captcha 76 | """ 77 | super().__init__(method=method, *args, **kwargs) 78 | 79 | self.create_task_payload["task"].update( 80 | {"websiteURL": websiteURL, "appId": appId, "apiServer": apiServer} 81 | ) 82 | 83 | # check user params 84 | if method not in atbCaptchaEnm.list_values(): 85 | raise ValueError(f"Invalid method parameter set, available - {atbCaptchaEnm.list_values()}") 86 | 87 | def captcha_handler(self, **kwargs) -> dict: 88 | """ 89 | Sync solving method 90 | 91 | Args: 92 | kwargs: additional params for `requests` library 93 | 94 | Returns: 95 | Dict with full server response 96 | 97 | Notes: 98 | Check class docstirng for more info 99 | """ 100 | 101 | return self._processing_response(**kwargs) 102 | 103 | async def aio_captcha_handler(self) -> dict: 104 | """ 105 | Async solving method 106 | 107 | Returns: 108 | Dict with full server response 109 | 110 | Notes: 111 | Check class docstirng for more info 112 | """ 113 | return await self._aio_processing_response() 114 | -------------------------------------------------------------------------------- /src/python_rucaptcha/captcha_fox.py: -------------------------------------------------------------------------------- 1 | from .core.base import BaseCaptcha 2 | from .core.enums import CaptchaFoxEnm 3 | 4 | 5 | class CaptchaFox(BaseCaptcha): 6 | def __init__( 7 | self, 8 | websiteURL: str, 9 | websiteKey: str, 10 | userAgent: str, 11 | proxyType: str, 12 | proxyAddress: str, 13 | proxyPort: str, 14 | *args, 15 | **kwargs, 16 | ): 17 | """ 18 | The class is used to work with CaptchaFox. 19 | 20 | Args: 21 | rucaptcha_key: User API key 22 | websiteURL: Full URL of the captcha page 23 | websiteKey: The value of the `key` parameter. 24 | It can be found in the page source code or captured in network requests during page loading. 25 | userAgent: User-Agent of your browser will be used to load the captcha. 26 | Use only modern browser's User-Agents 27 | proxyType: Proxy type - `http`, `socks4`, `socks5` 28 | proxyAddress: Proxy IP address or hostname 29 | proxyPort: Proxy port 30 | method: Captcha type 31 | kwargs: Not required params for task creation request 32 | 33 | Examples: 34 | >>> CaptchaFox(rucaptcha_key="aa9011f31111181111168611f1151122", 35 | ... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651", 36 | ... websiteKey="sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G", 37 | ... userAgent="Mozilla/5.0 .....", 38 | ... proxyType="socks5", 39 | ... proxyAddress="1.2.3.4", 40 | ... proxyPort="445", 41 | ... ).captcha_handler() 42 | { 43 | "errorId":0, 44 | "status":"ready", 45 | "solution":{ 46 | "token":"142000f.....er" 47 | }, 48 | "cost":"0.002", 49 | "ip":"1.2.3.4", 50 | "createTime":1692863536, 51 | "endTime":1692863556, 52 | "solveCount":0, 53 | "taskId": 73243152973, 54 | } 55 | 56 | >>> await CaptchaFox(rucaptcha_key="aa9011f31111181111168611f1151122", 57 | ... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651", 58 | ... websiteKey="sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G", 59 | ... userAgent="Mozilla/5.0 .....", 60 | ... proxyType="socks5", 61 | ... proxyAddress="1.2.3.4", 62 | ... proxyPort="445", 63 | ... ).aio_captcha_handler() 64 | { 65 | "errorId":0, 66 | "status":"ready", 67 | "solution":{ 68 | "token":"142000f.....er" 69 | }, 70 | "cost":"0.002", 71 | "ip":"1.2.3.4", 72 | "createTime":1692863536, 73 | "endTime":1692863556, 74 | "solveCount":0, 75 | "taskId": 73243152973, 76 | } 77 | 78 | Returns: 79 | Dict with full server response 80 | 81 | Notes: 82 | https://2captcha.com/api-docs/captchafox 83 | 84 | https://rucaptcha.com/api-docs/captchafox 85 | """ 86 | super().__init__(method=CaptchaFoxEnm.CaptchaFoxTask, *args, **kwargs) 87 | 88 | self.create_task_payload["task"].update( 89 | { 90 | "websiteURL": websiteURL, 91 | "websiteKey": websiteKey, 92 | "userAgent": userAgent, 93 | "proxyType": proxyType, 94 | "proxyAddress": proxyAddress, 95 | "proxyPort": proxyPort, 96 | } 97 | ) 98 | 99 | def captcha_handler(self, **kwargs) -> dict: 100 | """ 101 | Sync solving method 102 | 103 | Args: 104 | kwargs: additional params for `requests` library 105 | 106 | Returns: 107 | Dict with full server response 108 | 109 | Notes: 110 | Check class docstirng for more info 111 | """ 112 | return self._processing_response(**kwargs) 113 | 114 | async def aio_captcha_handler(self) -> dict: 115 | """ 116 | Async solving method 117 | 118 | Returns: 119 | Dict with full server response 120 | 121 | Notes: 122 | Check class docstirng for more info 123 | """ 124 | return await self._aio_processing_response() 125 | -------------------------------------------------------------------------------- /src/python_rucaptcha/capy_puzzle.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import CapyPuzzleEnm 5 | 6 | 7 | class CapyPuzzle(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websiteKey: str, 12 | method: Union[str, CapyPuzzleEnm] = CapyPuzzleEnm.CapyTaskProxyless, 13 | *args, 14 | **kwargs, 15 | ): 16 | """ 17 | The class is used to work with CapyPuzzle. 18 | 19 | Args: 20 | rucaptcha_key: User API key 21 | websiteURL: Full URL of the captcha page 22 | websiteKey: The value of the `captchakey` parameter you found in the code of the page 23 | method: Captcha type 24 | 25 | Examples: 26 | >>> CapyPuzzle(rucaptcha_key="aa9011f31111181111168611f1151122", 27 | ... websiteKey="PUZZLE_Cme4hZLjuZRMYC3uh14C52D3uNms5w", 28 | ... websiteURL="https://www.capy.me/account/register/", 29 | ... method=CapyPuzzleEnm.CapyTaskProxyless.value, 30 | ... api_server="https://jp.api.capy.me/", 31 | ... version="puzzle", 32 | ... ).captcha_handler() 33 | { 34 | "errorId":0, 35 | "status":"ready", 36 | "solution":{ 37 | "captchakey":"PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v", 38 | "challengekey":"qHAPtn68KTnXFM8VQ3mtYRtmy3cSKuHJ", 39 | "answer":"0xax8ex0xax84x0xkx7qx0xux7gx0xx42x0x3ox42x0x3ox4cx", 40 | "respKey":"" 41 | }, 42 | "cost":"0.00299", 43 | "ip":"1.2.3.4", 44 | "createTime":1692863536, 45 | "endTime":1692863556, 46 | "solveCount":1, 47 | "taskId":75190409731 48 | } 49 | 50 | >>> CapyPuzzle(rucaptcha_key="aa9011f31111181111168611f1151122", 51 | ... websiteKey="PUZZLE_Cme4hZLjuZRMYC3uh14C52D3uNms5w", 52 | ... websiteURL="https://www.capy.me/account/register/", 53 | ... method=CapyPuzzleEnm.CapyTaskProxyless.value, 54 | ... api_server="https://jp.api.capy.me/", 55 | ... version="avatar", 56 | ... ).captcha_handler() 57 | { 58 | "errorId":0, 59 | "status":"ready", 60 | "solution":{ 61 | "captchakey":"PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v", 62 | "challengekey":"qHAPtn68KTnXFM8VQ3mtYRtmy3cSKuHJ", 63 | "answer":"0xax8ex0xax84x0xkx7qx0xux7gx0xx42x0x3ox42x0x3ox4cx", 64 | "respKey":"" 65 | }, 66 | "cost":"0.00299", 67 | "ip":"1.2.3.4", 68 | "createTime":1692863536, 69 | "endTime":1692863556, 70 | "solveCount":1, 71 | "taskId":75190409731 72 | } 73 | 74 | >>> CapyPuzzle(rucaptcha_key="aa9011f31111181111168611f1151122", 75 | ... websiteKey="PUZZLE_Cme4hZLjuZRMYC3uh14C52D3uNms5w", 76 | ... websiteURL="https://www.capy.me/account/register/", 77 | ... method="CapyTaskProxyless", 78 | ... api_server="https://jp.api.capy.me/", 79 | ... version="puzzle", 80 | ... ).captcha_handler() 81 | { 82 | "errorId":0, 83 | "status":"ready", 84 | "solution":{ 85 | "captchakey":"PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v", 86 | "challengekey":"qHAPtn68KTnXFM8VQ3mtYRtmy3cSKuHJ", 87 | "answer":"0xax8ex0xax84x0xkx7qx0xux7gx0xx42x0x3ox42x0x3ox4cx", 88 | "respKey":"" 89 | }, 90 | "cost":"0.00299", 91 | "ip":"1.2.3.4", 92 | "createTime":1692863536, 93 | "endTime":1692863556, 94 | "solveCount":1, 95 | "taskId":75190409731 96 | } 97 | 98 | Returns: 99 | Dict with full server response 100 | 101 | Notes: 102 | https://rucaptcha.com/api-docs/capy-puzzle-captcha 103 | """ 104 | super().__init__(method=method, *args, **kwargs) 105 | 106 | self.create_task_payload["task"].update({"websiteURL": websiteURL, "websiteKey": websiteKey}) 107 | 108 | # check user params 109 | if method not in CapyPuzzleEnm.list_values(): 110 | raise ValueError(f"Invalid method parameter set, available - {CapyPuzzleEnm.list_values()}") 111 | 112 | def captcha_handler(self, **kwargs) -> dict: 113 | """ 114 | Sync solving method 115 | 116 | Args: 117 | kwargs: additional params for `requests` library 118 | 119 | Returns: 120 | Dict with full server response 121 | 122 | Notes: 123 | Check class docstirng for more info 124 | """ 125 | 126 | return self._processing_response(**kwargs) 127 | 128 | async def aio_captcha_handler(self) -> dict: 129 | """ 130 | Async solving method 131 | 132 | Returns: 133 | Dict with full server response 134 | 135 | Notes: 136 | Check class docstirng for more info 137 | """ 138 | return await self._aio_processing_response() 139 | -------------------------------------------------------------------------------- /src/python_rucaptcha/control.py: -------------------------------------------------------------------------------- 1 | from .core.base import BaseCaptcha 2 | from .core.enums import ControlEnm 3 | from .core.result_handler import get_sync_result, get_async_result 4 | 5 | 6 | class Control(BaseCaptcha): 7 | def __init__( 8 | self, 9 | *args, 10 | **kwargs, 11 | ): 12 | """ 13 | The class is used to work with RuCaptcha control methods. 14 | 15 | Args: 16 | action: Control action type 17 | 18 | Examples: 19 | >>> Control(rucaptcha_key="aa9.....").getBalance() 20 | { 21 | 'balance': 1593.4479 22 | } 23 | 24 | >>> await Control(rucaptcha_key="aa9.....").aio_getBalance() 25 | { 26 | 'balance': 1593.4479 27 | } 28 | 29 | >>> Control(rucaptcha_key="aa9.....").reportCorrect(id=75188571838) 30 | { 31 | 'errorId': 0, 32 | 'status': 'success' 33 | } 34 | 35 | >>> await Control(rucaptcha_key="aa9.....").aio_reportCorrect(id=75188571838) 36 | { 37 | 'errorId': 0, 38 | 'status': 'success' 39 | } 40 | 41 | >>> Control(rucaptcha_key="aa9.....").reportIncorrect(id=75188571838) 42 | { 43 | 'errorId': 0, 44 | 'status': 'success' 45 | } 46 | 47 | >>> await Control(rucaptcha_key="aa9.....").aio_reportIncorrect(id=75188571838) 48 | { 49 | 'errorId': 0, 50 | 'status': 'success' 51 | } 52 | 53 | Returns: 54 | Dict with full server response 55 | 56 | Notes: 57 | https://rucaptcha.com/api-docs/get-balance 58 | 59 | https://rucaptcha.com/api-docs/report-correct 60 | 61 | https://rucaptcha.com/api-docs/report-incorrect 62 | 63 | https://2captcha.com/api-docs/get-balance 64 | 65 | https://2captcha.com/api-docs/report-correct 66 | 67 | https://2captcha.com/api-docs/report-incorrect 68 | """ 69 | 70 | super().__init__(method=ControlEnm.control, *args, **kwargs) 71 | 72 | def reportCorrect(self, id: int) -> dict: 73 | """ 74 | reportCorrect method 75 | 76 | Args: 77 | id: Captcha task ID 78 | 79 | Returns: 80 | Dict with full server response 81 | 82 | Notes: 83 | https://2captcha.com/api-docs/report-correct 84 | """ 85 | self.get_task_payload.taskId = id 86 | return get_sync_result( 87 | get_payload=self.get_task_payload, 88 | sleep_time=self.params.sleep_time, 89 | url_response=f"https://api.{self.params.service_type}.com/reportCorrect", 90 | ) 91 | 92 | async def aio_reportCorrect(self, id: int) -> dict: 93 | """ 94 | Captcha results report 95 | 96 | Args: 97 | id: Captcha task ID 98 | 99 | Returns: 100 | Dict with full server response 101 | 102 | Notes: 103 | https://2captcha.com/api-docs/report-correct 104 | """ 105 | self.get_task_payload.taskId = id 106 | return await get_async_result( 107 | get_payload=self.get_task_payload, 108 | sleep_time=self.params.sleep_time, 109 | url_response=f"https://api.{self.params.service_type}.com/reportCorrect", 110 | ) 111 | 112 | def reportIncorrect(self, id: int) -> dict: 113 | """ 114 | reportCorrect method 115 | 116 | Args: 117 | id: Captcha task ID 118 | 119 | Returns: 120 | Dict with full server response 121 | 122 | Notes: 123 | https://2captcha.com/api-docs/report-incorrect 124 | """ 125 | self.get_task_payload.taskId = id 126 | return get_sync_result( 127 | get_payload=self.get_task_payload, 128 | sleep_time=self.params.sleep_time, 129 | url_response=f"https://api.{self.params.service_type}.com/reportIncorrect", 130 | ) 131 | 132 | async def aio_reportIncorrect(self, id: int) -> dict: 133 | """ 134 | Captcha results report 135 | 136 | Args: 137 | id: Captcha task ID 138 | 139 | Returns: 140 | Dict with full server response 141 | 142 | Notes: 143 | https://2captcha.com/api-docs/report-incorrect 144 | """ 145 | self.get_task_payload.taskId = id 146 | return await get_async_result( 147 | get_payload=self.get_task_payload, 148 | sleep_time=self.params.sleep_time, 149 | url_response=f"https://api.{self.params.service_type}.com/reportIncorrect", 150 | ) 151 | 152 | def getBalance(self) -> dict: 153 | """ 154 | GetBalance method 155 | 156 | Returns: 157 | Dict with full server response 158 | 159 | Notes: 160 | Check class docstirng for more info 161 | """ 162 | return get_sync_result( 163 | get_payload=self.get_task_payload, 164 | sleep_time=self.params.sleep_time, 165 | url_response=f"https://api.{self.params.service_type}.com/getBalance", 166 | ) 167 | 168 | async def aio_getBalance(self) -> dict: 169 | """ 170 | Async GetBalance method 171 | 172 | Returns: 173 | Dict with full server response 174 | 175 | Notes: 176 | Check class docstirng for more info 177 | """ 178 | return get_sync_result( 179 | get_payload=self.get_task_payload, 180 | sleep_time=self.params.sleep_time, 181 | url_response=f"https://api.{self.params.service_type}.com/getBalance", 182 | ) 183 | -------------------------------------------------------------------------------- /src/python_rucaptcha/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/src/python_rucaptcha/core/__init__.py -------------------------------------------------------------------------------- /src/python_rucaptcha/core/config.py: -------------------------------------------------------------------------------- 1 | from tenacity import AsyncRetrying, wait_fixed, stop_after_attempt 2 | from requests.adapters import Retry 3 | 4 | RETRIES = Retry(total=5, backoff_factor=0.5) 5 | ASYNC_RETRIES = AsyncRetrying(wait=wait_fixed(5), stop=stop_after_attempt(5), reraise=True) 6 | # Application key 7 | APP_KEY = "1899" 8 | 9 | 10 | # Connection retry generator 11 | def attempts_generator(amount: int = 20): 12 | """ 13 | Function generates a generator of length equal to `amount` 14 | 15 | Args: 16 | amount: number of attempts generated 17 | 18 | Returns: 19 | Attempt number 20 | """ 21 | yield from range(1, amount) 22 | -------------------------------------------------------------------------------- /src/python_rucaptcha/core/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | from typing import List 3 | 4 | 5 | class MyEnum(Enum): 6 | """ 7 | Base class for work with updated Enums 8 | """ 9 | 10 | @classmethod 11 | def list(cls) -> List[Enum]: 12 | return list(map(lambda c: c, cls)) 13 | 14 | @classmethod 15 | def list_values(cls) -> List[str]: 16 | return list(map(lambda c: c.value, cls)) 17 | 18 | @classmethod 19 | def list_names(cls) -> List[str]: 20 | return list(map(lambda c: c.name, cls)) 21 | 22 | 23 | class ServiceEnm(str, MyEnum): 24 | TWOCAPTCHA = "2captcha" 25 | RUCAPTCHA = "rucaptcha" 26 | DEATHBYCAPTCHA = "deathbycaptcha" 27 | 28 | 29 | class SaveFormatsEnm(str, MyEnum): 30 | TEMP = "temp" 31 | CONST = "const" 32 | 33 | 34 | class GeetestEnm(str, MyEnum): 35 | GeeTestTask = "GeeTestTask" 36 | GeeTestTaskProxyless = "GeeTestTaskProxyless" 37 | 38 | 39 | class ImageCaptchaEnm(str, MyEnum): 40 | ImageToTextTask = "ImageToTextTask" 41 | 42 | 43 | class CapyPuzzleEnm(str, MyEnum): 44 | CapyTask = "CapyTask" 45 | CapyTaskProxyless = "CapyTaskProxyless" 46 | 47 | 48 | class FunCaptchaEnm(str, MyEnum): 49 | FunCaptchaTaskProxyless = "FunCaptchaTaskProxyless" 50 | FunCaptchaTask = "FunCaptchaTask" 51 | 52 | 53 | class ReCaptchaEnm(str, MyEnum): 54 | RecaptchaV2TaskProxyless = "RecaptchaV2TaskProxyless" 55 | RecaptchaV2Task = "RecaptchaV2Task" 56 | 57 | RecaptchaV2EnterpriseTaskProxyless = "RecaptchaV2EnterpriseTaskProxyless" 58 | RecaptchaV2EnterpriseTask = "RecaptchaV2EnterpriseTask" 59 | 60 | RecaptchaV3TaskProxyless = "RecaptchaV3TaskProxyless" 61 | 62 | 63 | class LeminCaptchaEnm(str, MyEnum): 64 | LeminTaskProxyless = "LeminTaskProxyless" 65 | LeminTask = "LeminTask" 66 | 67 | 68 | class HCaptchaEnm(str, MyEnum): 69 | HCaptchaTask = "HCaptchaTask" 70 | HCaptchaTaskProxyless = "HCaptchaTaskProxyless" 71 | 72 | 73 | class KeyCaptchaEnm(str, MyEnum): 74 | KeyCaptchaTask = "KeyCaptchaTask" 75 | KeyCaptchaTaskProxyless = "KeyCaptchaTaskProxyless" 76 | 77 | 78 | class RotateCaptchaEnm(str, MyEnum): 79 | RotateTask = "RotateTask" 80 | 81 | 82 | class TikTokCaptchaEnm(str, MyEnum): 83 | TIKTOK = "tiktok" 84 | 85 | 86 | class ControlEnm(str, MyEnum): 87 | control = "control" 88 | # https://rucaptcha.com/api-docs/get-balance 89 | getBalance = "getBalance" 90 | # https://rucaptcha.com/api-docs/report-correct 91 | reportCorrect = "reportCorrect" 92 | # https://rucaptcha.com/api-docs/report-incorrect 93 | reportIncorrect = "reportIncorrect" 94 | 95 | 96 | class TurnstileCaptchaEnm(str, MyEnum): 97 | TurnstileTaskProxyless = "TurnstileTaskProxyless" 98 | TurnstileTask = "TurnstileTask" 99 | 100 | 101 | class AmazonWAFCaptchaEnm(str, MyEnum): 102 | AmazonTask = "AmazonTask" 103 | AmazonTaskProxyless = "AmazonTaskProxyless" 104 | 105 | 106 | class TextCaptchaEnm(str, MyEnum): 107 | TextCaptchaTask = "TextCaptchaTask" 108 | 109 | 110 | class AudioCaptchaEnm(str, MyEnum): 111 | AudioTask = "AudioTask" 112 | 113 | 114 | class CutCaptchaEnm(str, MyEnum): 115 | CutCaptchaTask = "CutCaptchaTask" 116 | CutCaptchaTaskProxyless = "CutCaptchaTaskProxyless" 117 | 118 | 119 | class DataDomeSliderEnm(str, MyEnum): 120 | DataDomeSliderTask = "DataDomeSliderTask" 121 | 122 | 123 | class CyberSiARAEnm(str, MyEnum): 124 | AntiCyberSiAraTask = "AntiCyberSiAraTask" 125 | AntiCyberSiAraTaskProxyless = "AntiCyberSiAraTaskProxyless" 126 | 127 | 128 | class MTCaptchaEnm(str, MyEnum): 129 | MtCaptchaTask = "MtCaptchaTask" 130 | MtCaptchaTaskProxyless = "MtCaptchaTaskProxyless" 131 | 132 | 133 | class BoundingBoxCaptchaEnm(str, MyEnum): 134 | BoundingBoxTask = "BoundingBoxTask" 135 | 136 | 137 | class DrawAroundCaptchaEnm(str, MyEnum): 138 | DrawAroundTask = "DrawAroundTask" 139 | 140 | 141 | class CoordinatesCaptchaEnm(str, MyEnum): 142 | CoordinatesTask = "CoordinatesTask" 143 | 144 | 145 | class GridCaptchaEnm(str, MyEnum): 146 | GridTask = "GridTask" 147 | 148 | 149 | class FriendlyCaptchaEnm(str, MyEnum): 150 | FriendlyCaptchaTaskProxyless = "FriendlyCaptchaTaskProxyless" 151 | FriendlyCaptchaTask = "FriendlyCaptchaTask" 152 | 153 | 154 | class TencentEnm(str, MyEnum): 155 | TencentTask = "TencentTask" 156 | TencentTaskProxyless = "TencentTaskProxyless" 157 | 158 | 159 | class atbCaptchaEnm(str, MyEnum): 160 | AtbCaptchaTask = "AtbCaptchaTask" 161 | AtbCaptchaTaskProxyless = "AtbCaptchaTaskProxyless" 162 | 163 | 164 | class ProsopoEnm(str, MyEnum): 165 | ProsopoTask = "ProsopoTask" 166 | ProsopoTaskProxyless = "ProsopoTaskProxyless " 167 | 168 | 169 | class CaptchaFoxEnm(str, MyEnum): 170 | CaptchaFoxTask = "CaptchaFoxTask" 171 | -------------------------------------------------------------------------------- /src/python_rucaptcha/core/result_handler.py: -------------------------------------------------------------------------------- 1 | import time 2 | import asyncio 3 | import logging 4 | from typing import Union 5 | 6 | import aiohttp 7 | import requests 8 | 9 | from .config import attempts_generator 10 | from .serializer import GetTaskResultRequestSer, GetTaskResultResponseSer 11 | 12 | 13 | def get_sync_result( 14 | get_payload: GetTaskResultRequestSer, sleep_time: int, url_response: str 15 | ) -> Union[dict, Exception]: 16 | """ 17 | Function periodically send the SYNC request to service and wait for captcha solving result 18 | """ 19 | # generator for repeated attempts to connect to the server 20 | attempts = attempts_generator() 21 | for _ in attempts: 22 | try: 23 | # send a request for the result of solving the captcha 24 | captcha_response = GetTaskResultResponseSer( 25 | **requests.post(url_response, json=get_payload.to_dict()).json(), taskId=get_payload.taskId 26 | ) 27 | logging.warning(f"{captcha_response = }") 28 | # if the captcha has not been resolved yet, wait 29 | if captcha_response.status == "processing": 30 | time.sleep(sleep_time) 31 | continue 32 | elif captcha_response.status == "ready": 33 | break 34 | elif captcha_response.errorId != 0: 35 | return captcha_response.to_dict() 36 | except Exception as error: 37 | return error 38 | return captcha_response.to_dict() 39 | 40 | 41 | async def get_async_result( 42 | get_payload: GetTaskResultRequestSer, sleep_time: int, url_response: str 43 | ) -> Union[dict, Exception]: 44 | """ 45 | Function periodically send the ASYNC request to service and wait for captcha solving result 46 | """ 47 | # generator for repeated attempts to connect to the server 48 | attempts = attempts_generator() 49 | async with aiohttp.ClientSession() as session: 50 | for _ in attempts: 51 | try: 52 | # send a request for the result of solving the captcha 53 | async with session.post( 54 | url_response, json=get_payload.to_dict(), raise_for_status=True 55 | ) as resp: 56 | captcha_response = await resp.json(content_type=None) 57 | captcha_response = GetTaskResultResponseSer(**captcha_response, taskId=get_payload.taskId) 58 | 59 | # if the captcha has not been resolved yet, wait 60 | if captcha_response.status == "processing": 61 | await asyncio.sleep(sleep_time) 62 | continue 63 | elif captcha_response.status == "ready": 64 | break 65 | elif captcha_response.errorId != 0: 66 | return captcha_response.to_dict() 67 | except Exception as error: 68 | return error 69 | return captcha_response.to_dict() 70 | -------------------------------------------------------------------------------- /src/python_rucaptcha/core/serializer.py: -------------------------------------------------------------------------------- 1 | from typing import Literal, Optional 2 | 3 | from msgspec import Struct 4 | 5 | from . import enums 6 | from .config import APP_KEY 7 | 8 | 9 | class MyBaseModel(Struct): 10 | def to_dict(self): 11 | return {f: getattr(self, f) for f in self.__struct_fields__} 12 | 13 | 14 | """ 15 | HTTP API Serializers 16 | """ 17 | 18 | 19 | class TaskSer(MyBaseModel): 20 | type: str 21 | 22 | 23 | class CreateTaskBaseSer(MyBaseModel): 24 | clientKey: str 25 | task: TaskSer = {} 26 | languagePool: str = "en" 27 | callbackUrl: str = None 28 | soft_id: Literal[APP_KEY] = APP_KEY 29 | 30 | 31 | class GetTaskResultRequestSer(MyBaseModel): 32 | clientKey: str 33 | taskId: int = None 34 | 35 | 36 | class CaptchaOptionsSer(MyBaseModel): 37 | sleep_time: int = 10 38 | service_type: enums.ServiceEnm = enums.ServiceEnm.TWOCAPTCHA.value 39 | 40 | url_request: Optional[str] = None 41 | url_response: Optional[str] = None 42 | 43 | def urls_set(self): 44 | """ 45 | Set request/response URLs if they not set previously 46 | """ 47 | if self.service_type == enums.ServiceEnm.DEATHBYCAPTCHA: 48 | self.url_request = f"http://api.{self.service_type}.com/2captcha/in.php" 49 | self.url_response = f"http://api.{self.service_type}.com/2captcha/res.php" 50 | else: 51 | self.url_request = f"https://api.{self.service_type}.com/createTask" 52 | self.url_response = f"https://api.{self.service_type}.com/getTaskResult" 53 | 54 | 55 | """ 56 | HTTP API Response 57 | """ 58 | 59 | 60 | class GetTaskResultResponseSer(MyBaseModel): 61 | status: str = "ready" 62 | solution: dict = None 63 | cost: float = None 64 | ip: str = None 65 | createTime: int = None 66 | endTime: int = None 67 | solveCount: int = None 68 | taskId: int = None 69 | # control method params 70 | balance: float = None 71 | # error info 72 | errorId: int = 0 73 | errorCode: str = None 74 | errorDescription: str = None 75 | -------------------------------------------------------------------------------- /src/python_rucaptcha/cutcaptcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import CutCaptchaEnm 5 | 6 | 7 | class CutCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | miseryKey: str, 12 | apiKey: str, 13 | method: Union[str, CutCaptchaEnm] = CutCaptchaEnm.CutCaptchaTaskProxyless, 14 | *args, 15 | **kwargs, 16 | ): 17 | """ 18 | The class is used to work with CutCaptcha. 19 | 20 | Args: 21 | rucaptcha_key: User API key 22 | websiteURL: Full URL of the captcha page 23 | miseryKey: The value of CUTCAPTCHA_MISERY_KEY variable defined on page. 24 | apiKey: The value of data-apikey attribute of iframe's body. 25 | Also the name of javascript file included on the page 26 | method: Captcha type 27 | kwargs: Not required params for task creation request 28 | 29 | Examples: 30 | >>> CutCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 31 | ... websiteURL="https://example.cc/foo/bar.html", 32 | ... miseryKey="a1488b66da00bf332a1488993a5443c79047e752", 33 | ... apiKey="SAb83IIB", 34 | ... method=CutCaptchaEnm.CutCaptchaTaskProxyless 35 | ... ).captcha_handler() 36 | { 37 | "errorId":0, 38 | "status":"ready", 39 | "solution":{ 40 | "token":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A", 41 | "respKey":"E0_eyJ0eXAiOiJK...y2w5_YbP8PGuJBBo", 42 | "userAgent":"Mozilla/5.0 (.......", 43 | "gRecaptchaResponse":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A" 44 | }, 45 | "cost":"0.00299", 46 | "ip":"1.2.3.4", 47 | "createTime":1692863536, 48 | "endTime":1692863556, 49 | "solveCount":1, 50 | "taskId": 73243152973, 51 | } 52 | 53 | >>> await CutCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 54 | ... websiteURL="https://example.cc/foo/bar.html", 55 | ... miseryKey="a1488b66da00bf332a1488993a5443c79047e752", 56 | ... apiKey="SAb83IIB", 57 | ... method=CutCaptchaEnm.CutCaptchaTaskProxyless 58 | ... ).aio_captcha_handler() 59 | { 60 | "errorId":0, 61 | "status":"ready", 62 | "solution":{ 63 | "token":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A", 64 | "respKey":"E0_eyJ0eXAiOiJK...y2w5_YbP8PGuJBBo", 65 | "userAgent":"Mozilla/5.0 (........", 66 | "gRecaptchaResponse":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A" 67 | }, 68 | "cost":"0.00299", 69 | "ip":"1.2.3.4", 70 | "createTime":1692863536, 71 | "endTime":1692863556, 72 | "solveCount":1, 73 | "taskId": 73243152973, 74 | } 75 | 76 | Returns: 77 | Dict with full server response 78 | 79 | Notes: 80 | https://2captcha.com/api-docs/cutcaptcha 81 | """ 82 | super().__init__(method=method, *args, **kwargs) 83 | 84 | self.create_task_payload["task"].update( 85 | {"websiteURL": websiteURL, "miseryKey": miseryKey, "apiKey": apiKey} 86 | ) 87 | 88 | # check user params 89 | if method not in CutCaptchaEnm.list_values(): 90 | raise ValueError(f"Invalid method parameter set, available - {CutCaptchaEnm.list_values()}") 91 | 92 | def captcha_handler(self, **kwargs) -> dict: 93 | """ 94 | Sync solving method 95 | 96 | Args: 97 | kwargs: Parameters for the `requests` library 98 | 99 | Returns: 100 | Dict with full server response 101 | 102 | Notes: 103 | Check class docstirng for more info 104 | """ 105 | 106 | return self._processing_response(**kwargs) 107 | 108 | async def aio_captcha_handler(self) -> dict: 109 | """ 110 | Async solving method 111 | 112 | Returns: 113 | Dict with full server response 114 | 115 | Notes: 116 | Check class docstirng for more info 117 | """ 118 | return await self._aio_processing_response() 119 | -------------------------------------------------------------------------------- /src/python_rucaptcha/cyber_siara_captcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import CyberSiARAEnm 5 | 6 | 7 | class CyberSiARACaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | SlideMasterUrlId: str, 12 | userAgent: str, 13 | method: Union[str, CyberSiARAEnm] = CyberSiARAEnm.AntiCyberSiAraTaskProxyless.value, 14 | *args, 15 | **kwargs, 16 | ): 17 | """ 18 | The class is used to work with HCaptcha. 19 | 20 | Args: 21 | rucaptcha_key: User API key 22 | websiteURL: Full URL of the captcha page 23 | SlideMasterUrlId: The value of the `MasterUrlId` parameter obtained from the request to the endpoint `API/CyberSiara/GetCyberSiara`. 24 | userAgent: User-Agent of your browser will be used to load the captcha. Use only modern browser's User-Agents 25 | method: Captcha type 26 | kwargs: Not required params for task creation request 27 | 28 | Examples: 29 | >>> CyberSiARACaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 30 | ... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651", 31 | ... SlideMasterUrlId="https://rucaptcha.com/demo/hcaptcha", 32 | ... userAgent="Mozilla/5.0 (Windows .....", 33 | ... method=CyberSiARAEnm.AntiCyberSiAraTaskProxyless, 34 | ... ).captcha_handler() 35 | { 36 | "errorId":0, 37 | "status":"ready", 38 | "solution":{ 39 | "token": "datadome=4ZXwCBlyHx9ktZhSnycMF...; Path=/; Secure; SameSite=Lax" 40 | }, 41 | "cost":"0.00299", 42 | "ip":"1.2.3.4", 43 | "createTime":1692863536, 44 | "endTime":1692863556, 45 | "solveCount":1, 46 | "taskId": 73243152973, 47 | } 48 | 49 | >>> await CyberSiARACaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 50 | ... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651", 51 | ... SlideMasterUrlId="https://rucaptcha.com/demo/hcaptcha", 52 | ... userAgent="Mozilla/5.0 (Windows .....", 53 | ... method=CyberSiARAEnm.AntiCyberSiAraTaskProxyless, 54 | ... ).aio_captcha_handler() 55 | { 56 | "errorId":0, 57 | "status":"ready", 58 | "solution":{ 59 | "token": "datadome=4ZXwCBlyHx9ktZhSnycMF...; Path=/; Secure; SameSite=Lax" 60 | }, 61 | "cost":"0.00299", 62 | "ip":"1.2.3.4", 63 | "createTime":1692863536, 64 | "endTime":1692863556, 65 | "solveCount":1, 66 | "taskId": 73243152973, 67 | } 68 | 69 | Returns: 70 | Dict with full server response 71 | 72 | Notes: 73 | https://rucaptcha.com/api-docs/anti-cyber-siara#cybersiara 74 | """ 75 | super().__init__(method=method, *args, **kwargs) 76 | 77 | self.create_task_payload["task"].update( 78 | {"websiteURL": websiteURL, "SlideMasterUrlId": SlideMasterUrlId, "userAgent": userAgent} 79 | ) 80 | # check user params 81 | if method not in CyberSiARAEnm.list_values(): 82 | raise ValueError(f"Invalid method parameter set, available - {CyberSiARAEnm.list_values()}") 83 | 84 | def captcha_handler(self, **kwargs) -> dict: 85 | """ 86 | Sync solving method 87 | 88 | Args: 89 | kwargs: Parameters for the `requests` library 90 | 91 | Returns: 92 | Dict with full server response 93 | 94 | Notes: 95 | Check class docstirng for more info 96 | """ 97 | 98 | return self._processing_response(**kwargs) 99 | 100 | async def aio_captcha_handler(self) -> dict: 101 | """ 102 | Async solving method 103 | 104 | Returns: 105 | Dict with full server response 106 | 107 | Notes: 108 | Check class docstirng for more info 109 | """ 110 | return await self._aio_processing_response() 111 | -------------------------------------------------------------------------------- /src/python_rucaptcha/datadome_captcha.py: -------------------------------------------------------------------------------- 1 | from .core.base import BaseCaptcha 2 | from .core.enums import DataDomeSliderEnm 3 | 4 | 5 | class DataDomeCaptcha(BaseCaptcha): 6 | def __init__( 7 | self, 8 | websiteURL: str, 9 | captchaUrl: str, 10 | userAgent: str, 11 | proxyType: str, 12 | proxyAddress: str, 13 | proxyPort: str, 14 | *args, 15 | **kwargs, 16 | ): 17 | """ 18 | The class is used to work with HCaptcha. 19 | 20 | Args: 21 | rucaptcha_key: User API key 22 | websiteURL: Full URL of the captcha page 23 | captchaUrl: The value of the `src` parameter for the `iframe` element 24 | containing the captcha on the page. 25 | userAgent: User-Agent of your browser will be used to load the captcha. 26 | Use only modern browser's User-Agents 27 | proxyType: Proxy type - `http`, `socks4`, `socks5` 28 | proxyAddress: Proxy IP address or hostname 29 | proxyPort: Proxy port 30 | method: Captcha type 31 | kwargs: Not required params for task creation request 32 | 33 | Examples: 34 | >>> DataDomeCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 35 | ... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651", 36 | ... captchaUrl="https://rucaptcha.com/demo/hcaptcha", 37 | ... userAgent="Mozilla/5.0 .....", 38 | ... proxyType="socks5", 39 | ... proxyAddress="1.2.3.4", 40 | ... proxyPort="445", 41 | ... ).captcha_handler() 42 | { 43 | "errorId":0, 44 | "status":"ready", 45 | "solution":{ 46 | "cookie": "datadome=4ZXwCBlyHx9ktZhSnycMF...; Path=/; Secure; SameSite=Lax" 47 | }, 48 | "cost":"0.00299", 49 | "ip":"1.2.3.4", 50 | "createTime":1692863536, 51 | "endTime":1692863556, 52 | "solveCount":1, 53 | "taskId": 73243152973, 54 | } 55 | 56 | >>> await DataDomeCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 57 | ... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651", 58 | ... captchaUrl="https://rucaptcha.com/demo/hcaptcha", 59 | ... userAgent="Mozilla/5.0 .....", 60 | ... proxyType="socks5", 61 | ... proxyAddress="1.2.3.4", 62 | ... proxyPort="445", 63 | ... ).aio_captcha_handler() 64 | { 65 | "errorId":0, 66 | "status":"ready", 67 | "solution":{ 68 | "cookie": "datadome=4ZXwCBlyHx9ktZhSnycMF...; Path=/; Secure; SameSite=Lax" 69 | }, 70 | "cost":"0.00299", 71 | "ip":"1.2.3.4", 72 | "createTime":1692863536, 73 | "endTime":1692863556, 74 | "solveCount":1, 75 | "taskId": 73243152973, 76 | } 77 | 78 | Returns: 79 | Dict with full server response 80 | 81 | Notes: 82 | https://rucaptcha.com/api-docs/datadome-slider-captcha 83 | """ 84 | super().__init__(method=DataDomeSliderEnm.DataDomeSliderTask, *args, **kwargs) 85 | 86 | self.create_task_payload["task"].update( 87 | { 88 | "websiteURL": websiteURL, 89 | "captchaUrl": captchaUrl, 90 | "userAgent": userAgent, 91 | "proxyType": proxyType, 92 | "proxyAddress": proxyAddress, 93 | "proxyPort": proxyPort, 94 | } 95 | ) 96 | 97 | def captcha_handler(self, **kwargs) -> dict: 98 | """ 99 | Sync solving method 100 | 101 | Args: 102 | kwargs: Parameters for the `requests` library 103 | 104 | Returns: 105 | Dict with full server response 106 | 107 | Notes: 108 | Check class docstirng for more info 109 | """ 110 | 111 | return self._processing_response(**kwargs) 112 | 113 | async def aio_captcha_handler(self) -> dict: 114 | """ 115 | Async solving method 116 | 117 | Returns: 118 | Dict with full server response 119 | 120 | Notes: 121 | Check class docstirng for more info 122 | """ 123 | return await self._aio_processing_response() 124 | -------------------------------------------------------------------------------- /src/python_rucaptcha/friendly_captcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import FriendlyCaptchaEnm 5 | 6 | 7 | class FriendlyCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websiteKey: str, 12 | method: Union[str, FriendlyCaptchaEnm] = FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless, 13 | *args, 14 | **kwargs, 15 | ): 16 | """ 17 | The class is used to work with Friendly Captcha. 18 | 19 | Args: 20 | rucaptcha_key: User API key 21 | websiteURL: The full URL of target web page where the captcha is loaded. We do not open the page, 22 | not a problem if it is available only for authenticated users 23 | websiteKey: The value of `data-sitekey` attribute of captcha's `div` element on page. 24 | method: Captcha type 25 | 26 | Examples: 27 | >>> FriendlyCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 28 | ... websiteKey="2FZFEVS1FZCGQ9", 29 | ... websiteURL="https://example.com", 30 | ... method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless.value 31 | ... ).captcha_handler() 32 | { 33 | "errorId":0, 34 | "status":"ready", 35 | "solution":{ 36 | "token":"PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v" 37 | }, 38 | "cost":"0.00299", 39 | "ip":"1.2.3.4", 40 | "createTime":1692863536, 41 | "endTime":1692863556, 42 | "solveCount":1, 43 | "taskId":75190409731 44 | } 45 | 46 | >>> FriendlyCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 47 | ... websiteKey="2FZFEVS1FZCGQ9", 48 | ... websiteURL="https://example.com", 49 | ... method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless.value 50 | ... ).captcha_handler() 51 | { 52 | "errorId":0, 53 | "status":"ready", 54 | "solution":{ 55 | "token":"PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v" 56 | }, 57 | "cost":"0.00299", 58 | "ip":"1.2.3.4", 59 | "createTime":1692863536, 60 | "endTime":1692863556, 61 | "solveCount":1, 62 | "taskId":75190409731 63 | } 64 | 65 | >>> await FriendlyCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 66 | ... websiteKey="2FZFEVS1FZCGQ9", 67 | ... websiteURL="https://example.com", 68 | ... method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless.value 69 | ... ).aio_captcha_handler() 70 | { 71 | "errorId":0, 72 | "status":"ready", 73 | "solution":{ 74 | "token":"PUZZLE_Abc1dEFghIJKLM2no34P56q7rStu8v" 75 | }, 76 | "cost":"0.00299", 77 | "ip":"1.2.3.4", 78 | "createTime":1692863536, 79 | "endTime":1692863556, 80 | "solveCount":1, 81 | "taskId":75190409731 82 | } 83 | 84 | Returns: 85 | Dict with full server response 86 | 87 | Notes: 88 | https://rucaptcha.com/api-docs/friendly-captcha 89 | """ 90 | super().__init__(method=method, *args, **kwargs) 91 | 92 | self.create_task_payload["task"].update({"websiteURL": websiteURL, "websiteKey": websiteKey}) 93 | 94 | # check user params 95 | if method not in FriendlyCaptchaEnm.list_values(): 96 | raise ValueError(f"Invalid method parameter set, available - {FriendlyCaptchaEnm.list_values()}") 97 | 98 | def captcha_handler(self, **kwargs) -> dict: 99 | """ 100 | Sync solving method 101 | 102 | Args: 103 | kwargs: additional params for `requests` library 104 | 105 | Returns: 106 | Dict with full server response 107 | 108 | Notes: 109 | Check class docstirng for more info 110 | """ 111 | 112 | return self._processing_response(**kwargs) 113 | 114 | async def aio_captcha_handler(self) -> dict: 115 | """ 116 | Async solving method 117 | 118 | Returns: 119 | Dict with full server response 120 | 121 | Notes: 122 | Check class docstirng for more info 123 | """ 124 | return await self._aio_processing_response() 125 | -------------------------------------------------------------------------------- /src/python_rucaptcha/fun_captcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import FunCaptchaEnm 5 | 6 | 7 | class FunCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websitePublicKey: str, 12 | method: Union[str, FunCaptchaEnm] = FunCaptchaEnm.FunCaptchaTaskProxyless, 13 | *args, 14 | **kwargs, 15 | ): 16 | """ 17 | The class is used to work with Arkose Labs FunCaptcha. 18 | 19 | Args: 20 | rucaptcha_key: User API key 21 | websiteURL: Full URL of the captcha page 22 | websitePublicKey: The value of the `pk` or `data-pkey` parameter you found in the page code 23 | method: Captcha type 24 | 25 | Examples: 26 | >>> FunCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 27 | ... websiteURL="https://api.funcaptcha.com/tile-game-lite-mode/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC&lang=en", 28 | ... websitePublicKey="69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", 29 | ... method=FunCaptchaEnm.FunCaptchaTaskProxyless.value 30 | ... ).captcha_handler() 31 | { 32 | "errorId":0, 33 | "status":"ready", 34 | "solution":{ 35 | "token":"142000f.....er" 36 | }, 37 | "cost":"0.002", 38 | "ip":"1.2.3.4", 39 | "createTime":1692863536, 40 | "endTime":1692863556, 41 | "solveCount":0, 42 | "taskId": 73243152973, 43 | } 44 | 45 | >>> await FunCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 46 | ... websiteURL="https://api.funcaptcha.com/tile-game-lite-mode/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC&lang=en", 47 | ... websitePublicKey="69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC", 48 | ... method=FunCaptchaEnm.FunCaptchaTaskProxyless.value 49 | ... ).aio_captcha_handler() 50 | { 51 | "errorId":0, 52 | "status":"ready", 53 | "solution":{ 54 | "token":"142000f.....er" 55 | }, 56 | "cost":"0.002", 57 | "ip":"1.2.3.4", 58 | "createTime":1692863536, 59 | "endTime":1692863556, 60 | "solveCount":0, 61 | "taskId": 73243152973, 62 | } 63 | 64 | Returns: 65 | Dict with full server response 66 | 67 | Notes: 68 | https://rucaptcha.com/api-docs/arkoselabs-funcaptcha 69 | """ 70 | super().__init__(method=method, *args, **kwargs) 71 | 72 | self.create_task_payload["task"].update( 73 | {"websiteURL": websiteURL, "websitePublicKey": websitePublicKey} 74 | ) 75 | 76 | # check user params 77 | if method not in FunCaptchaEnm.list_values(): 78 | raise ValueError(f"Invalid method parameter set, available - {FunCaptchaEnm.list_values()}") 79 | 80 | def captcha_handler(self, **kwargs) -> dict: 81 | """ 82 | Sync solving method 83 | 84 | Args: 85 | kwargs: additional params for `requests` library 86 | 87 | Returns: 88 | Dict with full server response 89 | 90 | Notes: 91 | Check class docstirng for more info 92 | """ 93 | return self._processing_response(**kwargs) 94 | 95 | async def aio_captcha_handler(self) -> dict: 96 | """ 97 | Async solving method 98 | 99 | Returns: 100 | Dict with full server response 101 | 102 | Notes: 103 | Check class docstirng for more info 104 | """ 105 | return await self._aio_processing_response() 106 | -------------------------------------------------------------------------------- /src/python_rucaptcha/hcaptcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import HCaptchaEnm 5 | 6 | 7 | class HCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websiteKey: str, 12 | method: Union[str, HCaptchaEnm] = HCaptchaEnm.HCaptchaTaskProxyless, 13 | *args, 14 | **kwargs, 15 | ): 16 | """ 17 | The class is used to work with HCaptcha. 18 | 19 | Args: 20 | rucaptcha_key: User API key 21 | websiteURL: Full URL of the captcha page 22 | websiteKey: The value of the `data-sitekey` parameter found on the site 23 | method: Captcha type 24 | kwargs: Not required params for task creation request 25 | 26 | Examples: 27 | >>> HCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 28 | ... websiteKey="3ceb8624-1970-4e6b-91d5-70317b70b651", 29 | ... websiteURL="https://rucaptcha.com/demo/hcaptcha", 30 | ... method=HCaptchaEnm.HCaptchaTaskProxyless.value 31 | ... ).captcha_handler() 32 | { 33 | "errorId":0, 34 | "status":"ready", 35 | "solution":{ 36 | "token":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A", 37 | "respKey":"E0_eyJ0eXAiOiJK...y2w5_YbP8PGuJBBo", 38 | "userAgent":"Mozilla/5.0 (.......", 39 | "gRecaptchaResponse":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A" 40 | }, 41 | "cost":"0.00299", 42 | "ip":"1.2.3.4", 43 | "createTime":1692863536, 44 | "endTime":1692863556, 45 | "solveCount":1, 46 | "taskId": 73243152973, 47 | } 48 | 49 | >>> await HCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 50 | ... websiteKey="3ceb8624-1970-4e6b-91d5-70317b70b651", 51 | ... websiteURL="https://rucaptcha.com/demo/hcaptcha", 52 | ... method=HCaptchaEnm.HCaptchaTaskProxyless.value 53 | ... ).aio_captcha_handler() 54 | { 55 | "errorId":0, 56 | "status":"ready", 57 | "solution":{ 58 | "token":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A", 59 | "respKey":"E0_eyJ0eXAiOiJK...y2w5_YbP8PGuJBBo", 60 | "userAgent":"Mozilla/5.0 (........", 61 | "gRecaptchaResponse":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A" 62 | }, 63 | "cost":"0.00299", 64 | "ip":"1.2.3.4", 65 | "createTime":1692863536, 66 | "endTime":1692863556, 67 | "solveCount":1, 68 | "taskId": 73243152973, 69 | } 70 | 71 | Returns: 72 | Dict with full server response 73 | 74 | Notes: 75 | https://rucaptcha.com/api-docs/hcaptcha 76 | """ 77 | super().__init__(method=method, *args, **kwargs) 78 | 79 | self.create_task_payload["task"].update({"websiteURL": websiteURL, "websiteKey": websiteKey}) 80 | 81 | # check user params 82 | if method not in HCaptchaEnm.list_values(): 83 | raise ValueError(f"Invalid method parameter set, available - {HCaptchaEnm.list_values()}") 84 | 85 | def captcha_handler(self, **kwargs) -> dict: 86 | """ 87 | Sync solving method 88 | 89 | Args: 90 | kwargs: Parameters for the `requests` library 91 | 92 | Returns: 93 | Dict with full server response 94 | 95 | Notes: 96 | Check class docstirng for more info 97 | """ 98 | 99 | return self._processing_response(**kwargs) 100 | 101 | async def aio_captcha_handler(self) -> dict: 102 | """ 103 | Async solving method 104 | 105 | Returns: 106 | Dict with full server response 107 | 108 | Notes: 109 | Check class docstirng for more info 110 | """ 111 | return await self._aio_processing_response() 112 | -------------------------------------------------------------------------------- /src/python_rucaptcha/key_captcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import KeyCaptchaEnm 5 | 6 | 7 | class KeyCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | s_s_c_user_id: str, 12 | s_s_c_session_id: str, 13 | s_s_c_web_server_sign: str, 14 | s_s_c_web_server_sign2: str, 15 | method: Union[str, KeyCaptchaEnm] = KeyCaptchaEnm.KeyCaptchaTaskProxyless, 16 | *args, 17 | **kwargs, 18 | ): 19 | """ 20 | The class is used to work with KeyCaptcha. 21 | 22 | Args: 23 | rucaptcha_key: User API key 24 | websiteURL: Full URL of the captcha page 25 | s_s_c_user_id: Value of `s_s_c_user_id` parameter found on the page 26 | s_s_c_session_id: Value of `s_s_c_session_id` parameter found on the page 27 | s_s_c_web_server_sign: Value of `s_s_c_web_server_sign` parameter found on the page 28 | s_s_c_web_server_sign2: Value of `s_s_c_web_server_sign2` parameter found on the page 29 | method: Captcha type 30 | kwargs: Not required params for task creation request 31 | 32 | Examples: 33 | >>> KeyCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 34 | ... pageurl="https://rucaptcha.com/demo/keycaptcha", 35 | ... s_s_c_user_id="184015", 36 | ... s_s_c_session_id="0917788cad24ad3a69813c4fcd556061", 37 | ... s_s_c_web_server_sign="02f7f9669f1269595c4c69bcd4a3c52e", 38 | ... s_s_c_web_server_sign2="d888700f6f324ec0f32b44c32c50bde1", 39 | ... method=KeyCaptchaEnm.KeyCaptchaTaskProxyless.value 40 | ... ).captcha_handler() 41 | { 42 | "captchaSolve": "d58....61|1", 43 | "taskId": 73052314114, 44 | "error": False, 45 | "errorBody": None 46 | } 47 | 48 | >>> await KeyCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 49 | ... pageurl="https://rucaptcha.com/demo/keycaptcha", 50 | ... s_s_c_user_id="184015", 51 | ... s_s_c_session_id="0917788cad24ad3a69813c4fcd556061", 52 | ... s_s_c_web_server_sign="02f7f9669f1269595c4c69bcd4a3c52e", 53 | ... s_s_c_web_server_sign2="d888700f6f324ec0f32b44c32c50bde1", 54 | ... method=KeyCaptchaEnm.KeyCaptchaTaskProxyless.value 55 | ... ).aio_captcha_handler() 56 | { 57 | "captchaSolve": "P1_eyJ.....cp_J", 58 | "taskId": 73052314114, 59 | "error": False, 60 | "errorBody": None 61 | } 62 | 63 | Returns: 64 | Dict with full server response 65 | 66 | Notes: 67 | https://rucaptcha.com/api-docs/keycaptcha 68 | """ 69 | super().__init__(method=method, *args, **kwargs) 70 | 71 | self.create_task_payload["task"].update( 72 | { 73 | "websiteURL": websiteURL, 74 | "s_s_c_user_id": s_s_c_user_id, 75 | "s_s_c_session_id": s_s_c_session_id, 76 | "s_s_c_web_server_sign": s_s_c_web_server_sign, 77 | "s_s_c_web_server_sign2": s_s_c_web_server_sign2, 78 | } 79 | ) 80 | 81 | # check user params 82 | if method not in KeyCaptchaEnm.list_values(): 83 | raise ValueError(f"Invalid method parameter set, available - {KeyCaptchaEnm.list_values()}") 84 | 85 | def captcha_handler(self, **kwargs) -> dict: 86 | """ 87 | Sync solving method 88 | 89 | Args: 90 | kwargs: Parameters for the `requests` library 91 | 92 | Returns: 93 | Dict with full server response 94 | 95 | Notes: 96 | Check class docstirng for more info 97 | """ 98 | 99 | return self._processing_response(**kwargs) 100 | 101 | async def aio_captcha_handler(self) -> dict: 102 | """ 103 | Async solving method 104 | 105 | Returns: 106 | Dict with full server response 107 | 108 | Notes: 109 | Check class docstirng for more info 110 | """ 111 | return await self._aio_processing_response() 112 | -------------------------------------------------------------------------------- /src/python_rucaptcha/lemin_captcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import LeminCaptchaEnm 5 | 6 | 7 | class LeminCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | captchaId: str, 12 | div_id: str, 13 | method: Union[str, LeminCaptchaEnm] = LeminCaptchaEnm.LeminTaskProxyless, 14 | *args, 15 | **kwargs, 16 | ): 17 | """ 18 | The class is used to work with Lemin Cropped Captcha. 19 | 20 | Args: 21 | rucaptcha_key: User API key 22 | websiteURL: Full URL of the captcha page 23 | captchaId: The value of the `captcha_id` parameter found on the site 24 | div_id: The `id` of the parent `div`, which contains the captcha 25 | method: Captcha type 26 | kwargs: Not required params for task creation request 27 | 28 | Examples: 29 | >>> LeminCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 30 | ... websiteURL="https://dashboard.leminnow.com/auth/signup", 31 | ... captchaId="CROPPED_099216d_8ba061383fa24ef498115023aa7189d4", 32 | ... div_id="lemin-cropped-captcha", 33 | ... method=LeminCaptchaEnm.LeminTaskProxyless.value, 34 | ... api_server="api.leminnow.com" 35 | ... ).captcha_handler() 36 | { 37 | "errorId":0, 38 | "status":"ready", 39 | "solution":{ 40 | "answer":"0xaxakx0xaxaax0xkxx3ox0x3ox3ox_...gAAAAABk8bgzEFOg9i3Jm", 41 | "challenge_id":"e0348984-92ec-23af-1488-446e3a58946c" 42 | }, 43 | "cost":"0.00299", 44 | "ip":"1.2.3.4", 45 | "createTime":1692863536, 46 | "endTime":1692863556, 47 | "solveCount":1, 48 | "taskId": 73243152973, 49 | } 50 | 51 | >>> await LeminCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 52 | ... websiteURL="https://dashboard.leminnow.com/auth/signup", 53 | ... captcha_id="CROPPED_099216d_8ba061383fa24ef498115023aa7189d4", 54 | ... div_id="lemin-cropped-captcha", 55 | ... method=LeminCaptchaEnm.LeminTaskProxyless.value, 56 | ... api_server="api.leminnow.com" 57 | ... ).aio_captcha_handler() 58 | { 59 | "errorId":0, 60 | "status":"ready", 61 | "solution":{ 62 | "answer":"0xaxakx0xaxaax0xkxx3ox0x3ox3ox_...gAAAAABk8bgzEFOg9i3Jm", 63 | "challenge_id":"e0348984-92ec-23af-1488-446e3a58946c" 64 | }, 65 | "cost":"0.00299", 66 | "ip":"1.2.3.4", 67 | "createTime":1692863536, 68 | "endTime":1692863556, 69 | "solveCount":1, 70 | "taskId": 73243152973, 71 | } 72 | 73 | Returns: 74 | Dict with full server response 75 | 76 | Notes: 77 | https://rucaptcha.com/api-docs/lemin 78 | """ 79 | super().__init__(method=method, *args, **kwargs) 80 | 81 | self.create_task_payload["task"].update( 82 | {"websiteURL": websiteURL, "captchaId": captchaId, "div_id": div_id} 83 | ) 84 | 85 | # check user params 86 | if method not in LeminCaptchaEnm.list_values(): 87 | raise ValueError(f"Invalid method parameter set, available - {LeminCaptchaEnm.list_values()}") 88 | 89 | def captcha_handler(self, **kwargs) -> dict: 90 | """ 91 | Sync solving method 92 | 93 | Args: 94 | kwargs: Parameters for the `requests` library 95 | 96 | Returns: 97 | Dict with full server response 98 | 99 | Notes: 100 | Check class docstirng for more info 101 | """ 102 | return self._processing_response(**kwargs) 103 | 104 | async def aio_captcha_handler(self) -> dict: 105 | """ 106 | Async solving method 107 | 108 | Returns: 109 | Dict with full server response 110 | 111 | Notes: 112 | Check class docstirng for more info 113 | """ 114 | return await self._aio_processing_response() 115 | -------------------------------------------------------------------------------- /src/python_rucaptcha/mt_captcha.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import MTCaptchaEnm 5 | 6 | 7 | class MTCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websiteKey: str, 12 | method: Union[str, MTCaptchaEnm] = MTCaptchaEnm.MtCaptchaTaskProxyless.value, 13 | *args, 14 | **kwargs, 15 | ): 16 | """ 17 | The class is used to work with HCaptcha. 18 | 19 | Args: 20 | rucaptcha_key: User API key 21 | websiteURL: Full URL of the captcha page 22 | websiteKey: The MTCaptcha `sitekey` value found in the page code. 23 | method: Captcha type 24 | kwargs: Not required params for task creation request 25 | 26 | Examples: 27 | >>> MTCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 28 | ... websiteURL="https://service.mtcaptcha.com/mtcv1/demo/index.html", 29 | ... websiteKey="MTPublic-DemoKey9M", 30 | ... method=MTCaptchaEnm.MtCaptchaTaskProxyless, 31 | ... ).captcha_handler() 32 | { 33 | "errorId":0, 34 | "status":"ready", 35 | "solution":{ 36 | "token": "datadome=4ZXwCBlyHx9ktZhSnycMF...; Path=/; Secure; SameSite=Lax" 37 | }, 38 | "cost":"0.00299", 39 | "ip":"1.2.3.4", 40 | "createTime":1692863536, 41 | "endTime":1692863556, 42 | "solveCount":1, 43 | "taskId": 73243152973, 44 | } 45 | 46 | >>> await MTCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", 47 | ... websiteURL="https://service.mtcaptcha.com/mtcv1/demo/index.html", 48 | ... websiteKey="MTPublic-DemoKey9M", 49 | ... method=MTCaptchaEnm.MtCaptchaTaskProxyless, 50 | ... ).aio_captcha_handler() 51 | { 52 | "errorId":0, 53 | "status":"ready", 54 | "solution":{ 55 | "token": "datadome=4ZXwCBlyHx9ktZhSnycMF...; Path=/; Secure; SameSite=Lax" 56 | }, 57 | "cost":"0.00299", 58 | "ip":"1.2.3.4", 59 | "createTime":1692863536, 60 | "endTime":1692863556, 61 | "solveCount":1, 62 | "taskId": 73243152973, 63 | } 64 | 65 | Returns: 66 | Dict with full server response 67 | 68 | Notes: 69 | https://2captcha.com/api-docs/mtcaptcha 70 | """ 71 | super().__init__(method=method, *args, **kwargs) 72 | 73 | self.create_task_payload["task"].update({"websiteURL": websiteURL, "websiteKey": websiteKey}) 74 | # check user params 75 | if method not in MTCaptchaEnm.list_values(): 76 | raise ValueError(f"Invalid method parameter set, available - {MTCaptchaEnm.list_values()}") 77 | 78 | def captcha_handler(self, **kwargs) -> dict: 79 | """ 80 | Sync solving method 81 | 82 | Args: 83 | kwargs: Parameters for the `requests` library 84 | 85 | Returns: 86 | Dict with full server response 87 | 88 | Notes: 89 | Check class docstirng for more info 90 | """ 91 | 92 | return self._processing_response(**kwargs) 93 | 94 | async def aio_captcha_handler(self) -> dict: 95 | """ 96 | Async solving method 97 | 98 | Returns: 99 | Dict with full server response 100 | 101 | Notes: 102 | Check class docstirng for more info 103 | """ 104 | return await self._aio_processing_response() 105 | -------------------------------------------------------------------------------- /src/python_rucaptcha/prosopo.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import ProsopoEnm 5 | 6 | 7 | class Prosopo(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websiteKey: str, 12 | method: Union[str, ProsopoEnm] = ProsopoEnm.ProsopoTaskProxyless, 13 | *args, 14 | **kwargs, 15 | ): 16 | """ 17 | The class is used to work with Prosopo. 18 | 19 | Args: 20 | rucaptcha_key: User API key 21 | websiteURL: The full URL of target web page where the captcha is loaded. 22 | We do not open the page, not a problem if it is available 23 | only for authenticated users 24 | websiteKey: The value of `siteKey` parameter found on the page. 25 | method: Captcha type 26 | 27 | Examples: 28 | >>> Prosopo(rucaptcha_key="aa9011f31111181111168611f1151122", 29 | ... websiteURL="https://www.example.com/", 30 | ... websiteKey="5EPQoMZEDc5LpN7gtxMMzYPTzA6UeWqL2stk1rso9gy4Ahqt", 31 | ... method=ProsopoEnm.ProsopoTaskProxyless.value, 32 | ... ).captcha_handler() 33 | { 34 | "errorId":0, 35 | "status":"ready", 36 | "solution":{ 37 | "token": "0x00016c68747470733950547a4136", 38 | }, 39 | "cost":"0.00299", 40 | "ip":"1.2.3.4", 41 | "createTime":1692863536, 42 | "endTime":1692863556, 43 | "solveCount":1, 44 | "taskId":75190409731 45 | } 46 | 47 | >>> await Prosopo(rucaptcha_key="aa9011f31111181111168611f1151122", 48 | ... websiteURL="https://www.example.com/", 49 | ... websiteKey="5EPQoMZEDc5LpN7gtxMMzYPTzA6UeWqL2stk1rso9gy4Ahqt", 50 | ... method=ProsopoEnm.ProsopoTaskProxyless.value, 51 | ... ).aio_captcha_handler() 52 | { 53 | "errorId":0, 54 | "status":"ready", 55 | "solution":{ 56 | "token": "0x00016c68747470733950547a4136", 57 | }, 58 | "cost":"0.00299", 59 | "ip":"1.2.3.4", 60 | "createTime":1692863536, 61 | "endTime":1692863556, 62 | "solveCount":1, 63 | "taskId":75190409731 64 | } 65 | 66 | Returns: 67 | Dict with full server response 68 | 69 | Notes: 70 | https://rucaptcha.com/api-docs/prosopo-procaptcha 71 | 72 | https://rucaptcha.com/api-docs/prosopo-procaptcha 73 | """ 74 | super().__init__(method=method, *args, **kwargs) 75 | 76 | self.create_task_payload["task"].update({"websiteURL": websiteURL, "websiteKey": websiteKey}) 77 | 78 | # check user params 79 | if method not in ProsopoEnm.list_values(): 80 | raise ValueError(f"Invalid method parameter set, available - {ProsopoEnm.list_values()}") 81 | 82 | def captcha_handler(self, **kwargs) -> dict: 83 | """ 84 | Sync solving method 85 | 86 | Args: 87 | kwargs: additional params for `requests` library 88 | 89 | Returns: 90 | Dict with full server response 91 | 92 | Notes: 93 | Check class docstirng for more info 94 | """ 95 | 96 | return self._processing_response(**kwargs) 97 | 98 | async def aio_captcha_handler(self) -> dict: 99 | """ 100 | Async solving method 101 | 102 | Returns: 103 | Dict with full server response 104 | 105 | Notes: 106 | Check class docstirng for more info 107 | """ 108 | return await self._aio_processing_response() 109 | -------------------------------------------------------------------------------- /src/python_rucaptcha/tencent.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import TencentEnm 5 | 6 | 7 | class Tencent(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | appId: str, 12 | method: Union[str, TencentEnm] = TencentEnm.TencentTaskProxyless, 13 | *args, 14 | **kwargs, 15 | ): 16 | """ 17 | The class is used to work with CapyPuzzle. 18 | 19 | Args: 20 | rucaptcha_key: User API key 21 | websiteURL: The full URL of target web page where the captcha is loaded. 22 | We do not open the page, not a problem if it is available 23 | only for authenticated users 24 | appId: The value of `appId` parameter in the website source code. 25 | method: Captcha type 26 | 27 | Examples: 28 | >>> Tencent(rucaptcha_key="aa9011f31111181111168611f1151122", 29 | ... websiteURL="https://www.tencentcloud.com/account/register", 30 | ... appId="2009899766", 31 | ... method=TencentEnm.TencentTaskProxyless.value, 32 | ... ).captcha_handler() 33 | { 34 | "errorId":0, 35 | "status":"ready", 36 | "solution":{ 37 | "appid": "190014885", 38 | "ret": 0, 39 | "ticket": "tr034XXXXXXXXXXXXXXXXXX*", 40 | "randstr": "@KVN" 41 | }, 42 | "cost":"0.00299", 43 | "ip":"1.2.3.4", 44 | "createTime":1692863536, 45 | "endTime":1692863556, 46 | "solveCount":1, 47 | "taskId":75190409731 48 | } 49 | 50 | >>> await Tencent(rucaptcha_key="aa9011f31111181111168611f1151122", 51 | ... websiteURL="https://www.tencentcloud.com/account/register", 52 | ... appId="2009899766", 53 | ... method=TencentEnm.TencentTaskProxyless.value, 54 | ... ).aio_captcha_handler() 55 | { 56 | "errorId":0, 57 | "status":"ready", 58 | "solution":{ 59 | "appid": "190014885", 60 | "ret": 0, 61 | "ticket": "tr034XXXXXXXXXXXXXXXXXX*", 62 | "randstr": "@KVN" 63 | }, 64 | "cost":"0.00299", 65 | "ip":"1.2.3.4", 66 | "createTime":1692863536, 67 | "endTime":1692863556, 68 | "solveCount":1, 69 | "taskId":75190409731 70 | } 71 | 72 | Returns: 73 | Dict with full server response 74 | 75 | Notes: 76 | https://rucaptcha.com/api-docs/tencent 77 | 78 | https://2captcha.com/api-docs/tencent 79 | """ 80 | super().__init__(method=method, *args, **kwargs) 81 | 82 | self.create_task_payload["task"].update({"websiteURL": websiteURL, "appId": appId}) 83 | 84 | # check user params 85 | if method not in TencentEnm.list_values(): 86 | raise ValueError(f"Invalid method parameter set, available - {TencentEnm.list_values()}") 87 | 88 | def captcha_handler(self, **kwargs) -> dict: 89 | """ 90 | Sync solving method 91 | 92 | Args: 93 | kwargs: additional params for `requests` library 94 | 95 | Returns: 96 | Dict with full server response 97 | 98 | Notes: 99 | Check class docstirng for more info 100 | """ 101 | 102 | return self._processing_response(**kwargs) 103 | 104 | async def aio_captcha_handler(self) -> dict: 105 | """ 106 | Async solving method 107 | 108 | Returns: 109 | Dict with full server response 110 | 111 | Notes: 112 | Check class docstirng for more info 113 | """ 114 | return await self._aio_processing_response() 115 | -------------------------------------------------------------------------------- /src/python_rucaptcha/text_captcha.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import TextCaptchaEnm 5 | 6 | 7 | class TextCaptcha(BaseCaptcha): 8 | def __init__( 9 | self, 10 | languagePool: str = "en", 11 | *args, 12 | **kwargs, 13 | ): 14 | """ 15 | The class is used to work with Text Captcha. 16 | 17 | Args: 18 | rucaptcha_key: User API key 19 | languagePool: Used to choose the workers for solving the captcha by their language. 20 | Applicable to image-based and text-based captchas.\n 21 | `en` - English-speaking workers\n 22 | `rn` - Russian-speaking workers. 23 | kwargs: Additional not required params for this captcha type 24 | 25 | Examples: 26 | >>> TextCaptcha(rucaptcha_key="aa90...51122", 27 | ... languagePool='en' 28 | ... ).captcha_handler(textcaptcha="If tomorrow is Saturday, what day is today?") 29 | { 30 | "errorId":0, 31 | "status":"ready", 32 | "solution":{ 33 | "text":"SUNDAY" 34 | }, 35 | "cost":0.03669, 36 | "ip":"46.53.241.91", 37 | "createTime":1695617910, 38 | "endTime":1695617965, 39 | "solveCount":2, 40 | "taskId":5423543 41 | } 42 | 43 | >>> TextCaptcha(rucaptcha_key="aa90...51122", 44 | ... ).captcha_handler(textcaptcha="If tomorrow is Saturday, what day is today?") 45 | { 46 | "errorId":0, 47 | "status":"ready", 48 | "solution":{ 49 | "text":"SUNDAY" 50 | }, 51 | "cost":0.03669, 52 | "ip":"46.53.241.91", 53 | "createTime":1695617910, 54 | "endTime":1695617965, 55 | "solveCount":2, 56 | "taskId":5423543 57 | } 58 | 59 | >>> await TextCaptcha(rucaptcha_key="aa90...51122", 60 | ... ).aio_captcha_handler(textcaptcha="If tomorrow is Saturday, what day is today?") 61 | { 62 | "errorId":0, 63 | "status":"ready", 64 | "solution":{ 65 | "text":"SUNDAY" 66 | }, 67 | "cost":0.03669, 68 | "ip":"46.53.241.91", 69 | "createTime":1695617910, 70 | "endTime":1695617965, 71 | "solveCount":2, 72 | "taskId":5423543 73 | } 74 | 75 | Returns: 76 | Dict with full server response 77 | 78 | Notes: 79 | https://2captcha.com/api-docs/text 80 | 81 | https://rucaptcha.com/api-docs/text 82 | """ 83 | 84 | super().__init__(method=TextCaptchaEnm.TextCaptchaTask.value, *args, **kwargs) 85 | 86 | self.create_task_payload.update({"languagePool": languagePool}) 87 | logging.warning(f"{self.create_task_payload = }") 88 | 89 | def captcha_handler(self, textcaptcha: str, **kwargs) -> dict: 90 | """ 91 | Synchronous method for captcha solving 92 | 93 | Args: 94 | textcaptcha: Captcha text 95 | 96 | Returns: 97 | Dict with full server response 98 | 99 | Notes: 100 | Check class docstirng for more info 101 | """ 102 | self.create_task_payload["task"].update({"comment": textcaptcha}) 103 | return self._processing_response(**kwargs) 104 | 105 | async def aio_captcha_handler(self, textcaptcha: str) -> dict: 106 | """ 107 | Asynchronous method for captcha solving 108 | 109 | Args: 110 | textcaptcha: Captcha text 111 | 112 | Returns: 113 | Dict with full server response 114 | 115 | Notes: 116 | Check class docstirng for more info 117 | """ 118 | self.create_task_payload["task"].update({"comment": textcaptcha}) 119 | return await self._aio_processing_response() 120 | -------------------------------------------------------------------------------- /src/python_rucaptcha/turnstile.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from .core.base import BaseCaptcha 4 | from .core.enums import TurnstileCaptchaEnm 5 | 6 | 7 | class Turnstile(BaseCaptcha): 8 | def __init__( 9 | self, 10 | websiteURL: str, 11 | websiteKey: str, 12 | userAgent: str, 13 | method: Union[str, TurnstileCaptchaEnm] = TurnstileCaptchaEnm.TurnstileTaskProxyless, 14 | *args, 15 | **kwargs, 16 | ): 17 | """ 18 | The class is used to work with Cloudflare Turnstile. 19 | 20 | Args: 21 | rucaptcha_key: User API key 22 | websiteURL: Full URL of the captcha page 23 | websiteKey: The value of the `sitekey` parameter found on the site 24 | userAgent: Your browser UserAgent 25 | method: Captcha type 26 | kwargs: Not required params for task creation request 27 | 28 | Examples: 29 | >>> Turnstile(rucaptcha_key="aa9011f31111181111168611f1151122", 30 | ... websiteURL="https://www.geetest.com/en/demo", 31 | ... websiteKey="0x4AAAAAAAC3DHQFLr1GavRN", 32 | ... method=TurnstileCaptchaEnm.TurnstileTaskProxyless.value, 33 | ... ).captcha_handler() 34 | { 35 | "errorId":0, 36 | "status":"ready", 37 | "solution":{ 38 | "token":"0.zrSnRHO7h0HwSjSCU8oyzbjEtD8p.d62306d4ee00c77dda697f959ebbd7bd97", 39 | "userAgent":"Mozilla/5.0 (....." 40 | }, 41 | "cost":"0.00145", 42 | "ip":"1.2.3.4", 43 | "createTime":1692863536, 44 | "endTime":1692863556, 45 | "solveCount":1, 46 | "taskId": 73243152973, 47 | } 48 | 49 | >>> await Turnstile(rucaptcha_key="aa9011f31111181111168611f1151122", 50 | ... websiteURL="https://www.geetest.com/en/demo", 51 | ... websiteKey="0x4AAAAAAAC3DHQFLr1GavRN", 52 | ... method=TurnstileCaptchaEnm.TurnstileTaskProxyless.value, 53 | ... ).aio_captcha_handler() 54 | { 55 | "errorId":0, 56 | "status":"ready", 57 | "solution":{ 58 | "token":"0.zrSnRHO7h0HwSjSCU8oyzbjEtD8p.d62306d4ee00c77dda697f959ebbd7bd97", 59 | "userAgent":"Mozilla/5.0 (....." 60 | }, 61 | "cost":"0.00145", 62 | "ip":"1.2.3.4", 63 | "createTime":1692863536, 64 | "endTime":1692863556, 65 | "solveCount":1, 66 | "taskId": 73243152973, 67 | } 68 | 69 | Returns: 70 | Dict with full server response 71 | 72 | Notes: 73 | https://rucaptcha.com/api-docs/cloudflare-turnstile 74 | """ 75 | 76 | super().__init__(method=method, *args, **kwargs) 77 | 78 | self.create_task_payload["task"].update( 79 | {"websiteURL": websiteURL, "websiteKey": websiteKey, "userAgent": userAgent} 80 | ) 81 | 82 | # check user params 83 | if method not in TurnstileCaptchaEnm.list_values(): 84 | raise ValueError(f"Invalid method parameter set, available - {TurnstileCaptchaEnm.list_values()}") 85 | 86 | def captcha_handler(self, **kwargs) -> dict: 87 | """ 88 | Sync solving method 89 | 90 | Args: 91 | kwargs: Parameters for the `requests` library 92 | 93 | Returns: 94 | Dict with full server response 95 | 96 | Notes: 97 | Check class docstirng for more info 98 | """ 99 | return self._processing_response(**kwargs) 100 | 101 | async def aio_captcha_handler(self) -> dict: 102 | """ 103 | Async solving method 104 | 105 | Returns: 106 | Dict with full server response 107 | 108 | Notes: 109 | Check class docstirng for more info 110 | """ 111 | return await self._aio_processing_response() 112 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AndreiDrang/python-rucaptcha/1c77f494aa55a477470eae0a643f92435d2cb5c8/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import time 3 | import random 4 | import string 5 | import logging 6 | 7 | import pytest 8 | 9 | 10 | @pytest.fixture(scope="function") 11 | def delay_func(): 12 | time.sleep(0.5) 13 | 14 | 15 | @pytest.fixture(scope="class") 16 | def delay_class(): 17 | time.sleep(3) 18 | 19 | 20 | @pytest.mark.usefixtures("delay_func") 21 | @pytest.mark.usefixtures("delay_class") 22 | class BaseTest: 23 | RUCAPTCHA_KEY = os.environ["RUCAPTCHA_KEY"] 24 | sleep_time = 5 25 | 26 | proxyAddress = "0.0.0.0" 27 | proxyPort = 9999 28 | 29 | def get_random_string(self, length: int) -> str: 30 | """ 31 | Method generate random string with set length 32 | : param length: Len of generated string 33 | : return: Random letter string 34 | """ 35 | # choose from all lowercase letter 36 | letters = string.ascii_lowercase 37 | result_str = "".join(random.choice(letters) for _ in range(length)) 38 | return result_str 39 | 40 | 41 | class DeathByTest(BaseTest): 42 | RUCAPTCHA_KEY = os.getenv("DEATHBYCAPTCHA_KEY") 43 | if not RUCAPTCHA_KEY: 44 | logging.warning("U do not set `DEATHBYCAPTCHA_KEY` ENV") 45 | -------------------------------------------------------------------------------- /tests/test_amazon.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.amazon_waf import AmazonWAF 5 | from python_rucaptcha.core.enums import AmazonWAFCaptchaEnm 6 | 7 | 8 | class TestAmazonCaptcha(BaseTest): 9 | pageurl = "https://captcha-api.yandex.ru/demo" 10 | sitekey = "FEXfAbHQsToo97VidNVk3j4dC74nGW1DgdxjtNB9" 11 | iv = "some-iv-value" 12 | context = "some-context-value" 13 | 14 | def test_methods_exists(self): 15 | assert "captcha_handler" in AmazonWAF.__dict__.keys() 16 | assert "aio_captcha_handler" in AmazonWAF.__dict__.keys() 17 | 18 | @pytest.mark.parametrize("method", AmazonWAFCaptchaEnm.list_values()) 19 | def test_args(self, method: str): 20 | instance = AmazonWAF( 21 | rucaptcha_key=self.RUCAPTCHA_KEY, 22 | websiteURL=self.pageurl, 23 | websiteKey=self.sitekey, 24 | iv=self.iv, 25 | context=self.context, 26 | method=method, 27 | ) 28 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 29 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 30 | assert instance.create_task_payload["task"]["websiteKey"] == self.sitekey 31 | assert instance.create_task_payload["task"]["iv"] == self.iv 32 | assert instance.create_task_payload["task"]["context"] == self.context 33 | assert instance.create_task_payload["task"]["type"] == method 34 | 35 | """ 36 | Success tests 37 | """ 38 | 39 | def test_basic_data(self): 40 | instance = AmazonWAF( 41 | rucaptcha_key=self.RUCAPTCHA_KEY, 42 | websiteURL=self.pageurl, 43 | websiteKey=self.sitekey, 44 | iv=self.iv, 45 | context=self.context, 46 | method=AmazonWAFCaptchaEnm.AmazonTaskProxyless.value, 47 | ) 48 | assert instance.captcha_handler() 49 | 50 | def test_context_basic_data(self): 51 | with AmazonWAF( 52 | rucaptcha_key=self.RUCAPTCHA_KEY, 53 | websiteURL=self.pageurl, 54 | websiteKey=self.sitekey, 55 | iv=self.iv, 56 | context=self.context, 57 | method=AmazonWAFCaptchaEnm.AmazonTaskProxyless.value, 58 | ) as instance: 59 | assert instance 60 | 61 | async def test_aio_basic_data(self): 62 | instance = AmazonWAF( 63 | rucaptcha_key=self.RUCAPTCHA_KEY, 64 | websiteURL=self.pageurl, 65 | websiteKey=self.sitekey, 66 | iv=self.iv, 67 | context=self.context, 68 | method=AmazonWAFCaptchaEnm.AmazonTaskProxyless.value, 69 | ) 70 | assert await instance.aio_captcha_handler() 71 | 72 | async def test_aio_context_basic_data(self): 73 | async with AmazonWAF( 74 | rucaptcha_key=self.RUCAPTCHA_KEY, 75 | websiteURL=self.pageurl, 76 | websiteKey=self.sitekey, 77 | iv=self.iv, 78 | context=self.context, 79 | method=AmazonWAFCaptchaEnm.AmazonTaskProxyless.value, 80 | ) as instance: 81 | assert instance 82 | 83 | """ 84 | Fail tests 85 | """ 86 | 87 | def test_wrong_method(self): 88 | with pytest.raises(ValueError): 89 | AmazonWAF( 90 | rucaptcha_key=self.RUCAPTCHA_KEY, 91 | websiteURL=self.pageurl, 92 | websiteKey=self.sitekey, 93 | iv=self.iv, 94 | context=self.context, 95 | method=self.get_random_string(5), 96 | ) 97 | -------------------------------------------------------------------------------- /tests/test_capypuzzle.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import CapyPuzzleEnm 5 | from python_rucaptcha.capy_puzzle import CapyPuzzle 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestCapyPuzzle(BaseTest): 10 | captchakey = "PUZZLE_Cme4hZLjuZRMYC3uh14C52D3uNms5w" 11 | pageurl = "https://www.capy.me/account/register/" 12 | api_server = "https://jp.api.capy.me/" 13 | versions = ["puzzle", "avatar"] 14 | 15 | def test_methods_exists(self): 16 | assert "captcha_handler" in CapyPuzzle.__dict__.keys() 17 | assert "aio_captcha_handler" in CapyPuzzle.__dict__.keys() 18 | 19 | @pytest.mark.parametrize("method", CapyPuzzleEnm.list_values()) 20 | def test_args(self, method: str): 21 | instance = CapyPuzzle( 22 | websiteURL=self.pageurl, 23 | websiteKey=self.captchakey, 24 | method=method, 25 | rucaptcha_key=self.RUCAPTCHA_KEY, 26 | api_server=self.api_server, 27 | version=self.versions[0], 28 | ) 29 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 30 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 31 | assert instance.create_task_payload["task"]["websiteKey"] == self.captchakey 32 | assert instance.create_task_payload["task"]["type"] == method 33 | 34 | """ 35 | Success tests 36 | """ 37 | 38 | def test_basic_data(self): 39 | instance = CapyPuzzle( 40 | websiteURL=self.pageurl, 41 | websiteKey=self.captchakey, 42 | method=CapyPuzzleEnm.CapyTaskProxyless.value, 43 | rucaptcha_key=self.RUCAPTCHA_KEY, 44 | api_server=self.api_server, 45 | version=self.versions[0], 46 | ) 47 | 48 | result = instance.captcha_handler() 49 | 50 | assert isinstance(result, dict) is True 51 | if not result["errorId"]: 52 | assert result["status"] in ("ready", "processing") 53 | assert isinstance(result["taskId"], int) is True 54 | else: 55 | assert result["errorId"] in (1, 12) 56 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 57 | 58 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 59 | 60 | async def test_aio_basic_data(self): 61 | instance = CapyPuzzle( 62 | websiteURL=self.pageurl, 63 | websiteKey=self.captchakey, 64 | method=CapyPuzzleEnm.CapyTaskProxyless.value, 65 | rucaptcha_key=self.RUCAPTCHA_KEY, 66 | api_server=self.api_server, 67 | version=self.versions[0], 68 | ) 69 | 70 | result = await instance.aio_captcha_handler() 71 | 72 | assert isinstance(result, dict) is True 73 | if not result["errorId"]: 74 | assert result["status"] in ("ready", "processing") 75 | assert isinstance(result["taskId"], int) is True 76 | else: 77 | assert result["errorId"] in (1, 12) 78 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 79 | 80 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 81 | 82 | def test_context_basic_data(self): 83 | with CapyPuzzle( 84 | websiteURL=self.pageurl, 85 | websiteKey=self.captchakey, 86 | method=CapyPuzzleEnm.CapyTaskProxyless.value, 87 | rucaptcha_key=self.RUCAPTCHA_KEY, 88 | api_server=self.api_server, 89 | version=self.versions[0], 90 | ) as instance: 91 | assert instance 92 | 93 | async def test_context_aio_basic_data(self): 94 | async with CapyPuzzle( 95 | websiteURL=self.pageurl, 96 | websiteKey=self.captchakey, 97 | method=CapyPuzzleEnm.CapyTaskProxyless.value, 98 | rucaptcha_key=self.RUCAPTCHA_KEY, 99 | api_server=self.api_server, 100 | version=self.versions[0], 101 | ) as instance: 102 | assert instance 103 | 104 | """ 105 | Fail tests 106 | """ 107 | 108 | def test_wrong_method(self): 109 | with pytest.raises(ValueError): 110 | CapyPuzzle( 111 | websiteURL=self.pageurl, 112 | websiteKey=self.captchakey, 113 | method=self.get_random_string(length=5), 114 | rucaptcha_key=self.RUCAPTCHA_KEY, 115 | ) 116 | -------------------------------------------------------------------------------- /tests/test_control.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.control import Control 5 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 6 | 7 | 8 | class TestControl(BaseTest): 9 | """ 10 | Success tests 11 | """ 12 | 13 | def test_methods_exists(self): 14 | assert "reportCorrect" in Control.__dict__.keys() 15 | assert "aio_reportCorrect" in Control.__dict__.keys() 16 | assert "reportIncorrect" in Control.__dict__.keys() 17 | assert "aio_reportIncorrect" in Control.__dict__.keys() 18 | assert "getBalance" in Control.__dict__.keys() 19 | assert "getBalance" in Control.__dict__.keys() 20 | 21 | def test_get_balance(self): 22 | instance = Control(rucaptcha_key=self.RUCAPTCHA_KEY) 23 | 24 | result = instance.getBalance() 25 | 26 | assert isinstance(result, dict) is True 27 | assert result["balance"] > 1 28 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 29 | 30 | def test_context_get_balance(self): 31 | with Control(rucaptcha_key=self.RUCAPTCHA_KEY) as instance: 32 | assert instance.getBalance() 33 | 34 | async def test_aio_get_balance(self): 35 | instance = Control(rucaptcha_key=self.RUCAPTCHA_KEY) 36 | 37 | result = await instance.aio_getBalance() 38 | 39 | assert isinstance(result, dict) is True 40 | assert result["balance"] > 1 41 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 42 | 43 | async def test_aio_context_get_balance(self): 44 | async with Control(rucaptcha_key=self.RUCAPTCHA_KEY) as instance: 45 | assert await instance.aio_getBalance() 46 | 47 | """ 48 | Failed tests 49 | """ 50 | 51 | def test_report_bad(self): 52 | instance = Control(rucaptcha_key=self.RUCAPTCHA_KEY) 53 | result = instance.reportIncorrect(id=random.randint(20, 50)) 54 | assert isinstance(result, dict) is True 55 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 56 | 57 | def test_context_report_bad(self): 58 | with Control(rucaptcha_key=self.RUCAPTCHA_KEY) as instance: 59 | assert instance.reportIncorrect(id=random.randint(20, 50)) 60 | 61 | async def test_aio_report_bad(self): 62 | instance = Control(rucaptcha_key=self.RUCAPTCHA_KEY) 63 | result = await instance.aio_reportIncorrect(id=random.randint(20, 50)) 64 | assert isinstance(result, dict) is True 65 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 66 | 67 | async def test_aio_context_report_bad(self): 68 | async with Control(rucaptcha_key=self.RUCAPTCHA_KEY) as instance: 69 | assert await instance.aio_reportIncorrect(id=random.randint(20, 50)) 70 | 71 | def test_report_good(self): 72 | instance = Control(rucaptcha_key=self.RUCAPTCHA_KEY) 73 | result = instance.reportCorrect(id=random.randint(20, 50)) 74 | assert isinstance(result, dict) is True 75 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 76 | 77 | def test_context_report_good(self): 78 | with Control(rucaptcha_key=self.RUCAPTCHA_KEY) as instance: 79 | assert instance.reportCorrect(id=random.randint(20, 50)) 80 | 81 | async def test_aio_report_good(self): 82 | instance = Control(rucaptcha_key=self.RUCAPTCHA_KEY) 83 | 84 | result = await instance.aio_reportCorrect(id=random.randint(20, 50)) 85 | 86 | assert isinstance(result, dict) is True 87 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 88 | 89 | async def test_aio_context_report_good(self): 90 | async with Control(rucaptcha_key=self.RUCAPTCHA_KEY) as instance: 91 | assert await instance.aio_reportCorrect(id=random.randint(20, 50)) 92 | -------------------------------------------------------------------------------- /tests/test_core.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from tenacity import AsyncRetrying 3 | from urllib3.util.retry import Retry 4 | 5 | from tests.conftest import BaseTest 6 | from python_rucaptcha.core.base import BaseCaptcha 7 | from python_rucaptcha.core.enums import MyEnum, ControlEnm, ServiceEnm 8 | from python_rucaptcha.core.config import RETRIES, ASYNC_RETRIES, attempts_generator 9 | 10 | 11 | class TestMain(BaseTest): 12 | """ 13 | Success tests 14 | """ 15 | 16 | def test_reties(self): 17 | assert isinstance(RETRIES, Retry) 18 | 19 | def test_async_reties(self): 20 | assert isinstance(ASYNC_RETRIES, AsyncRetrying) 21 | 22 | def test_context_class_create(self): 23 | with BaseCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, method=ControlEnm.control.value) as bc: 24 | pass 25 | 26 | def test_class_create(self): 27 | bc = BaseCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, method=ControlEnm.control.value) 28 | 29 | async def test_aio_context_class_create(self): 30 | async with BaseCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, method=ControlEnm.control.value) as bc: 31 | pass 32 | 33 | def test_custom_service(self): 34 | bc = BaseCaptcha( 35 | rucaptcha_key=self.RUCAPTCHA_KEY, 36 | method=ControlEnm.control.value, 37 | service_type=self.get_random_string(length=10), 38 | ) 39 | 40 | def test_context_custom_service(self): 41 | with BaseCaptcha( 42 | rucaptcha_key=self.RUCAPTCHA_KEY, 43 | method=ControlEnm.control.value, 44 | service_type=self.get_random_string(length=10), 45 | ) as bc: 46 | pass 47 | 48 | @pytest.mark.parametrize("elements", [31, 33]) 49 | def test_context_custom_service_api_key(self, elements): 50 | with BaseCaptcha( 51 | rucaptcha_key=self.get_random_string(elements), 52 | method=ControlEnm.control.value, 53 | service_type=self.get_random_string(length=10), 54 | ): 55 | pass 56 | 57 | @pytest.mark.parametrize("elements", [31, 33]) 58 | def test_custom_service_api_key(self, elements): 59 | BaseCaptcha( 60 | rucaptcha_key=self.get_random_string(elements), 61 | method=ControlEnm.control.value, 62 | service_type=self.get_random_string(length=10), 63 | ) 64 | 65 | @pytest.mark.parametrize("elements", [31, 33]) 66 | async def test_aio_context_custom_service_api_key(self, elements): 67 | async with BaseCaptcha( 68 | rucaptcha_key=self.get_random_string(elements), 69 | method=ControlEnm.control.value, 70 | service_type=self.get_random_string(length=10), 71 | ): 72 | pass 73 | 74 | """ 75 | Failed tests 76 | """ 77 | 78 | def test_context_err(self): 79 | with pytest.raises(ValueError): 80 | with BaseCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, method="some_method") as instance: 81 | raise ValueError 82 | 83 | async def test_aio_context_err(self): 84 | with pytest.raises(ValueError): 85 | async with BaseCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, method="some_method") as instance: 86 | raise ValueError 87 | 88 | 89 | class TestEnum(BaseTest): 90 | def test_enum_list(self): 91 | assert isinstance(MyEnum.list(), list) 92 | 93 | def test_enum_list_values(self): 94 | assert isinstance(MyEnum.list_values(), list) 95 | 96 | def test_enum_list_names(self): 97 | assert isinstance(MyEnum.list_names(), list) 98 | 99 | 100 | class TestConfig(BaseTest): 101 | def test_attempts_generator(self): 102 | attempt = None 103 | attempts = attempts_generator(amount=5) 104 | for attempt in attempts: 105 | assert isinstance(attempt, int) 106 | assert attempt == 4 107 | 108 | 109 | class TestDeathbycaptcha(BaseTest): 110 | def test_attempts_generator(self): 111 | BaseCaptcha( 112 | rucaptcha_key=self.RUCAPTCHA_KEY, 113 | service_type=ServiceEnm.DEATHBYCAPTCHA.value, 114 | method=ControlEnm.control.value, 115 | ) 116 | -------------------------------------------------------------------------------- /tests/test_cutcaptcha.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import CutCaptchaEnm 5 | from python_rucaptcha.cutcaptcha import CutCaptcha 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestCutCaptcha(BaseTest): 10 | miseryKey = "a1488b66da00bf332a1488993a5443c79047e752" 11 | pageurl = "https://example.cc/foo/bar.html" 12 | apiKey = "SAb83IIB" 13 | 14 | kwargs_params = { 15 | "proxyType": "socks5", 16 | "proxyAddress": BaseTest.proxyAddress, 17 | "proxyPort": BaseTest.proxyPort, 18 | } 19 | 20 | def test_methods_exists(self): 21 | assert "captcha_handler" in CutCaptcha.__dict__.keys() 22 | assert "aio_captcha_handler" in CutCaptcha.__dict__.keys() 23 | 24 | @pytest.mark.parametrize("method", CutCaptchaEnm.list_values()) 25 | def test_args(self, method: str): 26 | instance = CutCaptcha( 27 | rucaptcha_key=self.RUCAPTCHA_KEY, 28 | websiteURL=self.pageurl, 29 | miseryKey=self.miseryKey, 30 | apiKey=self.apiKey, 31 | method=method, 32 | ) 33 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 34 | assert instance.create_task_payload["task"]["type"] == method 35 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 36 | assert instance.create_task_payload["task"]["miseryKey"] == self.miseryKey 37 | assert instance.create_task_payload["task"]["apiKey"] == self.apiKey 38 | 39 | def test_kwargs(self): 40 | instance = CutCaptcha( 41 | rucaptcha_key=self.RUCAPTCHA_KEY, 42 | websiteURL=self.pageurl, 43 | miseryKey=self.miseryKey, 44 | apiKey=self.apiKey, 45 | method=CutCaptchaEnm.CutCaptchaTaskProxyless, 46 | **self.kwargs_params, 47 | ) 48 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 49 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 50 | 51 | """ 52 | Success tests 53 | """ 54 | 55 | def test_basic_data(self): 56 | instance = CutCaptcha( 57 | rucaptcha_key=self.RUCAPTCHA_KEY, 58 | websiteURL=self.pageurl, 59 | miseryKey=self.miseryKey, 60 | apiKey=self.apiKey, 61 | method=CutCaptchaEnm.CutCaptchaTaskProxyless.value, 62 | ) 63 | 64 | result = instance.captcha_handler() 65 | 66 | assert isinstance(result, dict) is True 67 | if not result["errorId"]: 68 | assert result["status"] in ("ready", "processing") 69 | assert isinstance(result["taskId"], int) is True 70 | else: 71 | assert result["errorId"] in (1, 12) 72 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 73 | 74 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 75 | 76 | async def test_aio_basic_data(self): 77 | instance = CutCaptcha( 78 | rucaptcha_key=self.RUCAPTCHA_KEY, 79 | websiteURL=self.pageurl, 80 | miseryKey=self.miseryKey, 81 | apiKey=self.apiKey, 82 | method=CutCaptchaEnm.CutCaptchaTaskProxyless.value, 83 | ) 84 | 85 | result = await instance.aio_captcha_handler() 86 | 87 | assert isinstance(result, dict) is True 88 | if not result["errorId"]: 89 | assert result["status"] in ("ready", "processing") 90 | assert isinstance(result["taskId"], int) is True 91 | else: 92 | assert result["errorId"] in (1, 12) 93 | assert result["errorCode"] in ("ERROR_CAPTCHA_UNSOLVABLE", CutCaptcha.NO_CAPTCHA_ERR) 94 | 95 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 96 | 97 | def test_context_basic_data(self): 98 | with CutCaptcha( 99 | rucaptcha_key=self.RUCAPTCHA_KEY, 100 | websiteURL=self.pageurl, 101 | miseryKey=self.miseryKey, 102 | apiKey=self.apiKey, 103 | method=CutCaptchaEnm.CutCaptchaTaskProxyless.value, 104 | ) as instance: 105 | assert instance 106 | 107 | async def test_context_aio_basic_data(self): 108 | async with CutCaptcha( 109 | rucaptcha_key=self.RUCAPTCHA_KEY, 110 | websiteURL=self.pageurl, 111 | miseryKey=self.miseryKey, 112 | apiKey=self.apiKey, 113 | method=CutCaptchaEnm.CutCaptchaTaskProxyless.value, 114 | ) as instance: 115 | assert instance 116 | 117 | """ 118 | Fail tests 119 | """ 120 | 121 | def test_wrong_method(self): 122 | with pytest.raises(ValueError): 123 | CutCaptcha( 124 | rucaptcha_key=self.RUCAPTCHA_KEY, 125 | websiteURL=self.pageurl, 126 | miseryKey=self.miseryKey, 127 | apiKey=self.apiKey, 128 | method=self.get_random_string(length=5), 129 | ) 130 | 131 | def test_no_websiteURL(self): 132 | with pytest.raises(TypeError): 133 | CutCaptcha( 134 | rucaptcha_key=self.RUCAPTCHA_KEY, 135 | miseryKey=self.miseryKey, 136 | apiKey=self.apiKey, 137 | method=self.get_random_string(length=5), 138 | ) 139 | 140 | def test_no_miseryKey(self): 141 | with pytest.raises(TypeError): 142 | CutCaptcha( 143 | rucaptcha_key=self.RUCAPTCHA_KEY, 144 | websiteURL=self.pageurl, 145 | apiKey=self.apiKey, 146 | method=self.get_random_string(length=5), 147 | ) 148 | 149 | def test_no_apiKey(self): 150 | with pytest.raises(TypeError): 151 | CutCaptcha( 152 | rucaptcha_key=self.RUCAPTCHA_KEY, 153 | websiteURL=self.pageurl, 154 | miseryKey=self.miseryKey, 155 | method=self.get_random_string(length=5), 156 | ) 157 | -------------------------------------------------------------------------------- /tests/test_cybersiara.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import CyberSiARAEnm 5 | from python_rucaptcha.cyber_siara_captcha import CyberSiARACaptcha 6 | 7 | 8 | class TestHCaptcha(BaseTest): 9 | websiteURL = "https://www.pokemoncenter.com/" 10 | SlideMasterUrlId = "OXR2LVNvCuXykkZbB8KZIfh162sNT8S2" 11 | userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" 12 | 13 | kwargs_params = { 14 | "proxyLogin": "user23", 15 | "proxyPassword": "p4$$w0rd", 16 | "proxyType": "socks5", 17 | "proxyAddress": BaseTest.proxyAddress, 18 | "proxyPort": BaseTest.proxyPort, 19 | } 20 | 21 | def test_methods_exists(self): 22 | assert "captcha_handler" in CyberSiARACaptcha.__dict__.keys() 23 | assert "aio_captcha_handler" in CyberSiARACaptcha.__dict__.keys() 24 | 25 | @pytest.mark.parametrize("method", CyberSiARAEnm.list_values()) 26 | def test_args(self, method: str): 27 | instance = CyberSiARACaptcha( 28 | rucaptcha_key=self.RUCAPTCHA_KEY, 29 | websiteURL=self.websiteURL, 30 | SlideMasterUrlId=self.SlideMasterUrlId, 31 | userAgent=self.userAgent, 32 | method=method, 33 | ) 34 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 35 | assert instance.create_task_payload["task"]["type"] == method 36 | assert instance.create_task_payload["task"]["websiteURL"] == self.websiteURL 37 | assert instance.create_task_payload["task"]["SlideMasterUrlId"] == self.SlideMasterUrlId 38 | assert instance.create_task_payload["task"]["userAgent"] == self.userAgent 39 | 40 | def test_kwargs(self): 41 | instance = CyberSiARACaptcha( 42 | rucaptcha_key=self.RUCAPTCHA_KEY, 43 | websiteURL=self.websiteURL, 44 | SlideMasterUrlId=self.SlideMasterUrlId, 45 | userAgent=self.userAgent, 46 | method=CyberSiARAEnm.AntiCyberSiAraTaskProxyless, 47 | **self.kwargs_params, 48 | ) 49 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 50 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 51 | 52 | """ 53 | Fail tests 54 | """ 55 | 56 | def test_no_websiteURL(self): 57 | with pytest.raises(TypeError): 58 | CyberSiARACaptcha( 59 | rucaptcha_key=self.RUCAPTCHA_KEY, 60 | SlideMasterUrlId=self.SlideMasterUrlId, 61 | userAgent=self.userAgent, 62 | method=CyberSiARAEnm.AntiCyberSiAraTaskProxyless, 63 | ) 64 | 65 | def test_no_SlideMasterUrlId(self): 66 | with pytest.raises(TypeError): 67 | CyberSiARACaptcha( 68 | rucaptcha_key=self.RUCAPTCHA_KEY, 69 | websiteURL=self.websiteURL, 70 | userAgent=self.userAgent, 71 | method=CyberSiARAEnm.AntiCyberSiAraTaskProxyless, 72 | ) 73 | 74 | def test_no_userAgent(self): 75 | with pytest.raises(TypeError): 76 | CyberSiARACaptcha( 77 | rucaptcha_key=self.RUCAPTCHA_KEY, 78 | websiteURL=self.websiteURL, 79 | SlideMasterUrlId=self.SlideMasterUrlId, 80 | method=CyberSiARAEnm.AntiCyberSiAraTaskProxyless, 81 | ) 82 | 83 | def test_wrong_method(self): 84 | with pytest.raises(ValueError): 85 | CyberSiARACaptcha( 86 | rucaptcha_key=self.RUCAPTCHA_KEY, 87 | websiteURL=self.websiteURL, 88 | SlideMasterUrlId=self.SlideMasterUrlId, 89 | userAgent=self.userAgent, 90 | method=self.get_random_string(length=5), 91 | ) 92 | -------------------------------------------------------------------------------- /tests/test_datadome.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import DataDomeSliderEnm 5 | from python_rucaptcha.datadome_captcha import DataDomeCaptcha 6 | 7 | 8 | class TestDatadome(BaseTest): 9 | websiteURL = "https://www.pokemoncenter.com/" 10 | captchaUrl = "https://geo.captcha-delivery.com/captcha/?initialCid=AHrlqAAAAAMAlk-FmAyNOW8AUyTH_g%3D%3D&hash=5B45875B653A484CC79E57036CE9FC&cid=noJuZstmvINksqOxaXWQogbPBd01y3VaH3r-CZ4eqK4roZuelJMHVhO2rR0IySRieoAivkg74B4UpJ.xj.jVNB6-aLaW.Bwvik7__EncryD6COavwx8RmOqgZ7DK_3v&t=fe&referer=https%3A%2F%2Fwww.pokemoncenter.com%2F&s=9817&e=2b1d5a78107ded0dcdc8317aa879979ed5083a2b3a95b734dbe7871679e1403" 11 | userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" 12 | proxyType = "http" 13 | proxyAddress = "1.2.3.4" 14 | proxyPort = "8080" 15 | kwargs_params = {"proxyLogin": "user23", "proxyPassword": "p4$$w0rd"} 16 | 17 | def test_methods_exists(self): 18 | assert "captcha_handler" in DataDomeCaptcha.__dict__.keys() 19 | assert "aio_captcha_handler" in DataDomeCaptcha.__dict__.keys() 20 | 21 | def test_args(self): 22 | instance = DataDomeCaptcha( 23 | rucaptcha_key=self.RUCAPTCHA_KEY, 24 | websiteURL=self.websiteURL, 25 | captchaUrl=self.captchaUrl, 26 | userAgent=self.userAgent, 27 | proxyType=self.proxyType, 28 | proxyAddress=self.proxyAddress, 29 | proxyPort=self.proxyPort, 30 | ) 31 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 32 | assert instance.create_task_payload["task"]["type"] == DataDomeSliderEnm.DataDomeSliderTask 33 | assert instance.create_task_payload["task"]["websiteURL"] == self.websiteURL 34 | assert instance.create_task_payload["task"]["captchaUrl"] == self.captchaUrl 35 | assert instance.create_task_payload["task"]["userAgent"] == self.userAgent 36 | assert instance.create_task_payload["task"]["proxyType"] == self.proxyType 37 | assert instance.create_task_payload["task"]["proxyAddress"] == self.proxyAddress 38 | assert instance.create_task_payload["task"]["proxyPort"] == self.proxyPort 39 | 40 | def test_kwargs(self): 41 | instance = DataDomeCaptcha( 42 | rucaptcha_key=self.RUCAPTCHA_KEY, 43 | websiteURL=self.websiteURL, 44 | captchaUrl=self.captchaUrl, 45 | userAgent=self.userAgent, 46 | proxyType=self.proxyType, 47 | proxyAddress=self.proxyAddress, 48 | proxyPort=self.proxyPort, 49 | **self.kwargs_params, 50 | ) 51 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 52 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 53 | 54 | """ 55 | Fail tests 56 | """ 57 | 58 | def test_no_websiteURL(self): 59 | with pytest.raises(TypeError): 60 | DataDomeCaptcha( 61 | rucaptcha_key=self.RUCAPTCHA_KEY, 62 | captchaUrl=self.captchaUrl, 63 | userAgent=self.userAgent, 64 | proxyType=self.proxyType, 65 | proxyAddress=self.proxyAddress, 66 | proxyPort=self.proxyPort, 67 | ) 68 | 69 | def test_no_captchaUrl(self): 70 | with pytest.raises(TypeError): 71 | DataDomeCaptcha( 72 | rucaptcha_key=self.RUCAPTCHA_KEY, 73 | websiteURL=self.websiteURL, 74 | userAgent=self.userAgent, 75 | proxyType=self.proxyType, 76 | proxyAddress=self.proxyAddress, 77 | proxyPort=self.proxyPort, 78 | ) 79 | 80 | def test_no_userAgent(self): 81 | with pytest.raises(TypeError): 82 | DataDomeCaptcha( 83 | rucaptcha_key=self.RUCAPTCHA_KEY, 84 | websiteURL=self.websiteURL, 85 | captchaUrl=self.captchaUrl, 86 | proxyType=self.proxyType, 87 | proxyAddress=self.proxyAddress, 88 | proxyPort=self.proxyPort, 89 | ) 90 | 91 | def test_no_proxyType(self): 92 | with pytest.raises(TypeError): 93 | DataDomeCaptcha( 94 | rucaptcha_key=self.RUCAPTCHA_KEY, 95 | websiteURL=self.websiteURL, 96 | captchaUrl=self.captchaUrl, 97 | userAgent=self.userAgent, 98 | proxyAddress=self.proxyAddress, 99 | proxyPort=self.proxyPort, 100 | ) 101 | 102 | def test_no_proxyAddress(self): 103 | with pytest.raises(TypeError): 104 | DataDomeCaptcha( 105 | rucaptcha_key=self.RUCAPTCHA_KEY, 106 | websiteURL=self.websiteURL, 107 | captchaUrl=self.captchaUrl, 108 | userAgent=self.userAgent, 109 | proxyType=self.proxyType, 110 | proxyPort=self.proxyPort, 111 | ) 112 | 113 | def test_no_proxyPort(self): 114 | with pytest.raises(TypeError): 115 | DataDomeCaptcha( 116 | rucaptcha_key=self.RUCAPTCHA_KEY, 117 | websiteURL=self.websiteURL, 118 | captchaUrl=self.captchaUrl, 119 | userAgent=self.userAgent, 120 | proxyType=self.proxyType, 121 | proxyAddress=self.proxyAddress, 122 | ) 123 | -------------------------------------------------------------------------------- /tests/test_friendly_captcha.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import FriendlyCaptchaEnm 5 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 6 | from python_rucaptcha.friendly_captcha import FriendlyCaptcha 7 | 8 | 9 | class TestFriendlyCaptcha(BaseTest): 10 | websiteURL = "https://example.cc/foo/bar.html" 11 | websiteKey = "SAb83IIB" 12 | 13 | kwargs_params = { 14 | "proxyType": "socks5", 15 | "proxyAddress": BaseTest.proxyAddress, 16 | "proxyPort": BaseTest.proxyPort, 17 | } 18 | 19 | def test_methods_exists(self): 20 | assert "captcha_handler" in FriendlyCaptcha.__dict__.keys() 21 | assert "aio_captcha_handler" in FriendlyCaptcha.__dict__.keys() 22 | 23 | @pytest.mark.parametrize("method", FriendlyCaptchaEnm.list_values()) 24 | def test_args(self, method: str): 25 | instance = FriendlyCaptcha( 26 | rucaptcha_key=self.RUCAPTCHA_KEY, 27 | websiteURL=self.websiteURL, 28 | websiteKey=self.websiteKey, 29 | method=method, 30 | ) 31 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 32 | assert instance.create_task_payload["task"]["type"] == method 33 | assert instance.create_task_payload["task"]["websiteURL"] == self.websiteURL 34 | assert instance.create_task_payload["task"]["websiteKey"] == self.websiteKey 35 | 36 | def test_kwargs(self): 37 | instance = FriendlyCaptcha( 38 | rucaptcha_key=self.RUCAPTCHA_KEY, 39 | websiteURL=self.websiteURL, 40 | websiteKey=self.websiteKey, 41 | method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless, 42 | **self.kwargs_params, 43 | ) 44 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 45 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 46 | 47 | """ 48 | Success tests 49 | """ 50 | 51 | def test_basic_data(self): 52 | instance = FriendlyCaptcha( 53 | rucaptcha_key=self.RUCAPTCHA_KEY, 54 | websiteURL=self.websiteURL, 55 | websiteKey=self.websiteKey, 56 | method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless.value, 57 | ) 58 | 59 | result = instance.captcha_handler() 60 | 61 | assert isinstance(result, dict) is True 62 | if not result["errorId"]: 63 | assert result["status"] in ("ready", "processing") 64 | assert isinstance(result["taskId"], int) is True 65 | else: 66 | assert result["errorId"] in (1, 12) 67 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 68 | 69 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 70 | 71 | async def test_aio_basic_data(self): 72 | instance = FriendlyCaptcha( 73 | rucaptcha_key=self.RUCAPTCHA_KEY, 74 | websiteURL=self.websiteURL, 75 | websiteKey=self.websiteKey, 76 | method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless.value, 77 | ) 78 | 79 | result = await instance.aio_captcha_handler() 80 | 81 | assert isinstance(result, dict) is True 82 | if not result["errorId"]: 83 | assert result["status"] in ("ready", "processing") 84 | assert isinstance(result["taskId"], int) is True 85 | else: 86 | assert result["errorId"] in (1, 12) 87 | assert result["errorCode"] in ("ERROR_CAPTCHA_UNSOLVABLE", FriendlyCaptcha.NO_CAPTCHA_ERR) 88 | 89 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 90 | 91 | def test_context_basic_data(self): 92 | with FriendlyCaptcha( 93 | rucaptcha_key=self.RUCAPTCHA_KEY, 94 | websiteURL=self.websiteURL, 95 | websiteKey=self.websiteKey, 96 | method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless.value, 97 | ) as instance: 98 | assert instance 99 | 100 | async def test_context_aio_basic_data(self): 101 | async with FriendlyCaptcha( 102 | rucaptcha_key=self.RUCAPTCHA_KEY, 103 | websiteURL=self.websiteURL, 104 | websiteKey=self.websiteKey, 105 | method=FriendlyCaptchaEnm.FriendlyCaptchaTaskProxyless.value, 106 | ) as instance: 107 | assert instance 108 | 109 | """ 110 | Fail tests 111 | """ 112 | 113 | def test_wrong_method(self): 114 | with pytest.raises(ValueError): 115 | FriendlyCaptcha( 116 | rucaptcha_key=self.RUCAPTCHA_KEY, 117 | websiteURL=self.websiteURL, 118 | websiteKey=self.websiteKey, 119 | method=self.get_random_string(length=5), 120 | ) 121 | 122 | def test_no_websiteURL(self): 123 | with pytest.raises(TypeError): 124 | FriendlyCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, websiteKey=self.websiteKey) 125 | 126 | def test_no_websiteKey(self): 127 | with pytest.raises(TypeError): 128 | FriendlyCaptcha( 129 | rucaptcha_key=self.RUCAPTCHA_KEY, 130 | websiteURL=self.websiteURL, 131 | ) 132 | -------------------------------------------------------------------------------- /tests/test_funcaptcha.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import FunCaptchaEnm 5 | from python_rucaptcha.fun_captcha import FunCaptcha 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestFunCaptcha(BaseTest): 10 | publickey = "69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC" 11 | pageurl = "https://api.funcaptcha.com/tile-game-lite-mode/fc/api/nojs/?pkey=69A21A01-CC7B-B9C6-0F9A-E7FA06677FFC&lang=en" 12 | surl = "https://client-api.arkoselabs.com" 13 | kwargs_params = { 14 | "funcaptchaApiJSSubdomain": "sample-api.arkoselabs.com", 15 | "userAgent": "Some specific user agent", 16 | "proxyType": "socks5", 17 | "proxyAddress": BaseTest.proxyAddress, 18 | "proxyPort": BaseTest.proxyPort, 19 | } 20 | 21 | def test_methods_exists(self): 22 | assert "captcha_handler" in FunCaptcha.__dict__.keys() 23 | assert "aio_captcha_handler" in FunCaptcha.__dict__.keys() 24 | 25 | @pytest.mark.parametrize("method", FunCaptchaEnm.list_values()) 26 | def test_args(self, method: str): 27 | instance = FunCaptcha( 28 | rucaptcha_key=self.RUCAPTCHA_KEY, 29 | websiteURL=self.pageurl, 30 | websitePublicKey=self.publickey, 31 | method=method, 32 | ) 33 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 34 | assert instance.create_task_payload["task"]["type"] == method 35 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 36 | assert instance.create_task_payload["task"]["websitePublicKey"] == self.publickey 37 | 38 | def test_kwargs(self): 39 | instance = FunCaptcha( 40 | rucaptcha_key=self.RUCAPTCHA_KEY, 41 | websiteURL=self.pageurl, 42 | websitePublicKey=self.publickey, 43 | method=FunCaptchaEnm.FunCaptchaTaskProxyless, 44 | **self.kwargs_params, 45 | ) 46 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 47 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 48 | 49 | """ 50 | Success tests 51 | """ 52 | 53 | def test_basic_data(self): 54 | instance = FunCaptcha( 55 | rucaptcha_key=self.RUCAPTCHA_KEY, 56 | websiteURL=self.pageurl, 57 | websitePublicKey=self.publickey, 58 | method=FunCaptchaEnm.FunCaptchaTaskProxyless.value, 59 | ) 60 | 61 | result = instance.captcha_handler() 62 | 63 | assert isinstance(result, dict) is True 64 | if not result["errorId"]: 65 | assert result["status"] == "ready" 66 | assert isinstance(result["solution"], dict) is True 67 | assert isinstance(result["taskId"], int) is True 68 | else: 69 | assert result["errorId"] in (1, 12) 70 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 71 | 72 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 73 | 74 | async def test_aio_basic_data(self): 75 | instance = FunCaptcha( 76 | rucaptcha_key=self.RUCAPTCHA_KEY, 77 | websiteURL=self.pageurl, 78 | websitePublicKey=self.publickey, 79 | method=FunCaptchaEnm.FunCaptchaTaskProxyless.value, 80 | ) 81 | 82 | result = await instance.aio_captcha_handler() 83 | 84 | assert isinstance(result, dict) is True 85 | if not result["errorId"]: 86 | assert result["status"] == "ready" 87 | assert isinstance(result["solution"], dict) is True 88 | assert isinstance(result["taskId"], int) is True 89 | else: 90 | assert result["errorId"] in (1, 12) 91 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 92 | 93 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 94 | 95 | def test_context_basic_data(self): 96 | with FunCaptcha( 97 | rucaptcha_key=self.RUCAPTCHA_KEY, 98 | websiteURL=self.pageurl, 99 | websitePublicKey=self.publickey, 100 | method=FunCaptchaEnm.FunCaptchaTaskProxyless.value, 101 | ) as instance: 102 | assert instance 103 | 104 | async def test_context_aio_basic_data(self): 105 | async with FunCaptcha( 106 | rucaptcha_key=self.RUCAPTCHA_KEY, 107 | websiteURL=self.pageurl, 108 | websitePublicKey=self.publickey, 109 | method=FunCaptchaEnm.FunCaptchaTaskProxyless.value, 110 | ) as instance: 111 | assert instance 112 | 113 | """ 114 | Fail tests 115 | """ 116 | 117 | def test_wrong_method(self): 118 | with pytest.raises(ValueError): 119 | FunCaptcha( 120 | rucaptcha_key=self.RUCAPTCHA_KEY, 121 | websiteURL=self.pageurl, 122 | websitePublicKey=self.publickey, 123 | method=self.get_random_string(length=5), 124 | ) 125 | -------------------------------------------------------------------------------- /tests/test_hcaptcha.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.hcaptcha import HCaptcha 5 | from python_rucaptcha.core.enums import HCaptchaEnm 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestHCaptcha(BaseTest): 10 | sitekey = "3ceb8624-1970-4e6b-91d5-70317b70b651" 11 | pageurl = "https://rucaptcha.com/demo/hcaptcha" 12 | kwargs_params = { 13 | "isInvisible": False, 14 | "userAgent": "Some specific user agent", 15 | "proxyType": "socks5", 16 | "proxyAddress": BaseTest.proxyAddress, 17 | "proxyPort": BaseTest.proxyPort, 18 | } 19 | 20 | def test_methods_exists(self): 21 | assert "captcha_handler" in HCaptcha.__dict__.keys() 22 | assert "aio_captcha_handler" in HCaptcha.__dict__.keys() 23 | 24 | @pytest.mark.parametrize("method", HCaptchaEnm.list_values()) 25 | def test_args(self, method: str): 26 | instance = HCaptcha( 27 | rucaptcha_key=self.RUCAPTCHA_KEY, 28 | websiteURL=self.pageurl, 29 | websiteKey=self.sitekey, 30 | method=method, 31 | ) 32 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 33 | assert instance.create_task_payload["task"]["type"] == method 34 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 35 | assert instance.create_task_payload["task"]["websiteKey"] == self.sitekey 36 | 37 | def test_kwargs(self): 38 | instance = HCaptcha( 39 | rucaptcha_key=self.RUCAPTCHA_KEY, 40 | websiteURL=self.pageurl, 41 | websiteKey=self.sitekey, 42 | method=HCaptchaEnm.HCaptchaTaskProxyless, 43 | **self.kwargs_params, 44 | ) 45 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 46 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 47 | 48 | """ 49 | Success tests 50 | """ 51 | 52 | def test_basic_data(self): 53 | instance = HCaptcha( 54 | rucaptcha_key=self.RUCAPTCHA_KEY, 55 | websiteURL=self.pageurl, 56 | websiteKey=self.sitekey, 57 | method=HCaptchaEnm.HCaptchaTaskProxyless.value, 58 | ) 59 | 60 | result = instance.captcha_handler() 61 | 62 | assert isinstance(result, dict) is True 63 | if not result["errorId"]: 64 | assert result["status"] == "ready" 65 | assert isinstance(result["solution"], dict) is True 66 | assert isinstance(result["taskId"], int) is True 67 | else: 68 | assert result["errorId"] in (1, 12) 69 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 70 | 71 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 72 | 73 | async def test_aio_basic_data(self): 74 | instance = HCaptcha( 75 | rucaptcha_key=self.RUCAPTCHA_KEY, 76 | websiteURL=self.pageurl, 77 | websiteKey=self.sitekey, 78 | method=HCaptchaEnm.HCaptchaTaskProxyless.value, 79 | ) 80 | 81 | result = await instance.aio_captcha_handler() 82 | 83 | assert isinstance(result, dict) is True 84 | if not result["errorId"]: 85 | assert result["status"] == "ready" 86 | assert isinstance(result["solution"], dict) is True 87 | assert isinstance(result["taskId"], int) is True 88 | else: 89 | assert result["errorId"] in (1, 12) 90 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 91 | 92 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 93 | 94 | def test_context_basic_data(self): 95 | with HCaptcha( 96 | rucaptcha_key=self.RUCAPTCHA_KEY, 97 | websiteURL=self.pageurl, 98 | websiteKey=self.sitekey, 99 | method=HCaptchaEnm.HCaptchaTaskProxyless.value, 100 | ) as instance: 101 | assert instance 102 | 103 | async def test_context_aio_basic_data(self): 104 | async with HCaptcha( 105 | rucaptcha_key=self.RUCAPTCHA_KEY, 106 | websiteURL=self.pageurl, 107 | websiteKey=self.sitekey, 108 | method=HCaptchaEnm.HCaptchaTaskProxyless.value, 109 | ) as instance: 110 | assert instance 111 | 112 | """ 113 | Fail tests 114 | """ 115 | 116 | def test_wrong_method(self): 117 | with pytest.raises(ValueError): 118 | HCaptcha( 119 | rucaptcha_key=self.RUCAPTCHA_KEY, 120 | websiteURL=self.pageurl, 121 | websiteKey=self.sitekey, 122 | method=self.get_random_string(length=5), 123 | ) 124 | -------------------------------------------------------------------------------- /tests/test_lemin.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import LeminCaptchaEnm 5 | from python_rucaptcha.lemin_captcha import LeminCaptcha 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestLeminCroppedCaptcha(BaseTest): 10 | pageurl = "https://dashboard.leminnow.com/auth/signup" 11 | api_server = "api.leminnow.com" 12 | div_id = "lemin-cropped-captcha" 13 | captcha_id = "CROPPED_099216d_8ba061383fa24ef498115023aa7189d4" 14 | kwargs_params = { 15 | "leminApiServerSubdomain": "https://api.leminnow.com/", 16 | "userAgent": "Some specific user agent", 17 | "proxyType": "socks5", 18 | "proxyAddress": BaseTest.proxyAddress, 19 | "proxyPort": BaseTest.proxyPort, 20 | } 21 | 22 | def test_methods_exists(self): 23 | assert "captcha_handler" in LeminCaptcha.__dict__.keys() 24 | assert "aio_captcha_handler" in LeminCaptcha.__dict__.keys() 25 | 26 | @pytest.mark.parametrize("method", LeminCaptchaEnm.list_values()) 27 | def test_args(self, method: str): 28 | instance = LeminCaptcha( 29 | rucaptcha_key=self.RUCAPTCHA_KEY, 30 | websiteURL=self.pageurl, 31 | captchaId=self.captcha_id, 32 | div_id=self.div_id, 33 | method=method, 34 | ) 35 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 36 | assert instance.create_task_payload["task"]["type"] == method 37 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 38 | 39 | def test_kwargs(self): 40 | instance = LeminCaptcha( 41 | rucaptcha_key=self.RUCAPTCHA_KEY, 42 | websiteURL=self.pageurl, 43 | captchaId=self.captcha_id, 44 | div_id=self.div_id, 45 | method=LeminCaptchaEnm.LeminTaskProxyless, 46 | **self.kwargs_params, 47 | ) 48 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 49 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 50 | 51 | """ 52 | Success tests 53 | """ 54 | 55 | def test_basic_data(self): 56 | instance = LeminCaptcha( 57 | rucaptcha_key=self.RUCAPTCHA_KEY, 58 | websiteURL=self.pageurl, 59 | captchaId=self.captcha_id, 60 | div_id=self.div_id, 61 | api_server=self.api_server, 62 | method=LeminCaptchaEnm.LeminTaskProxyless.value, 63 | ) 64 | 65 | result = instance.captcha_handler() 66 | 67 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 68 | 69 | async def test_aio_basic_data(self): 70 | instance = LeminCaptcha( 71 | rucaptcha_key=self.RUCAPTCHA_KEY, 72 | websiteURL=self.pageurl, 73 | captchaId=self.captcha_id, 74 | div_id=self.div_id, 75 | api_server=self.api_server, 76 | method=LeminCaptchaEnm.LeminTaskProxyless.value, 77 | ) 78 | 79 | result = await instance.aio_captcha_handler() 80 | 81 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 82 | 83 | def test_context_basic_data(self): 84 | with LeminCaptcha( 85 | rucaptcha_key=self.RUCAPTCHA_KEY, 86 | websiteURL=self.pageurl, 87 | captchaId=self.captcha_id, 88 | div_id=self.div_id, 89 | method=LeminCaptchaEnm.LeminTaskProxyless.value, 90 | ) as instance: 91 | assert instance 92 | 93 | async def test_context_aio_basic_data(self): 94 | async with LeminCaptcha( 95 | rucaptcha_key=self.RUCAPTCHA_KEY, 96 | websiteURL=self.pageurl, 97 | captchaId=self.captcha_id, 98 | div_id=self.div_id, 99 | method=LeminCaptchaEnm.LeminTaskProxyless.value, 100 | ) as instance: 101 | assert instance 102 | 103 | """ 104 | Fail tests 105 | """ 106 | 107 | def test_wrong_method(self): 108 | with pytest.raises(ValueError): 109 | LeminCaptcha( 110 | rucaptcha_key=self.RUCAPTCHA_KEY, 111 | websiteURL=self.pageurl, 112 | captchaId=self.captcha_id, 113 | div_id=self.div_id, 114 | api_server=self.api_server, 115 | method=self.get_random_string(length=5), 116 | ) 117 | 118 | def test_no_websiteURL(self): 119 | with pytest.raises(TypeError): 120 | LeminCaptcha( 121 | rucaptcha_key=self.RUCAPTCHA_KEY, 122 | captchaId=self.captcha_id, 123 | div_id=self.div_id, 124 | method=self.get_random_string(length=5), 125 | ) 126 | 127 | def test_no_div_id(self): 128 | with pytest.raises(TypeError): 129 | LeminCaptcha( 130 | rucaptcha_key=self.RUCAPTCHA_KEY, 131 | websiteURL=self.pageurl, 132 | captchaId=self.captcha_id, 133 | method=self.get_random_string(length=5), 134 | ) 135 | 136 | def test_no_captchaId(self): 137 | with pytest.raises(TypeError): 138 | LeminCaptcha( 139 | rucaptcha_key=self.RUCAPTCHA_KEY, 140 | websiteURL=self.pageurl, 141 | div_id=self.div_id, 142 | method=self.get_random_string(length=5), 143 | ) 144 | -------------------------------------------------------------------------------- /tests/test_mtcaptcha.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import MTCaptchaEnm 5 | from python_rucaptcha.mt_captcha import MTCaptcha 6 | 7 | 8 | class TestMTCaptcha(BaseTest): 9 | websiteURL = "https://service.mtcaptcha.com/mtcv1/demo/index.html" 10 | websiteKey = "MTPublic-DemoKey9M" 11 | 12 | kwargs_params = { 13 | "proxyType": "socks5", 14 | "proxyAddress": BaseTest.proxyAddress, 15 | "proxyPort": BaseTest.proxyPort, 16 | } 17 | 18 | def test_methods_exists(self): 19 | assert "captcha_handler" in MTCaptcha.__dict__.keys() 20 | assert "aio_captcha_handler" in MTCaptcha.__dict__.keys() 21 | 22 | @pytest.mark.parametrize("method", MTCaptchaEnm.list_values()) 23 | def test_args(self, method: str): 24 | instance = MTCaptcha( 25 | rucaptcha_key=self.RUCAPTCHA_KEY, 26 | websiteURL=self.websiteURL, 27 | websiteKey=self.websiteKey, 28 | method=method, 29 | ) 30 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 31 | assert instance.create_task_payload["task"]["type"] == method 32 | assert instance.create_task_payload["task"]["websiteURL"] == self.websiteURL 33 | assert instance.create_task_payload["task"]["websiteKey"] == self.websiteKey 34 | 35 | def test_kwargs(self): 36 | instance = MTCaptcha( 37 | rucaptcha_key=self.RUCAPTCHA_KEY, 38 | websiteURL=self.websiteURL, 39 | websiteKey=self.websiteKey, 40 | **self.kwargs_params, 41 | ) 42 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 43 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 44 | 45 | """ 46 | Fail tests 47 | """ 48 | 49 | def test_no_websiteURL(self): 50 | with pytest.raises(TypeError): 51 | MTCaptcha( 52 | rucaptcha_key=self.RUCAPTCHA_KEY, 53 | websiteKey=self.websiteKey, 54 | ) 55 | 56 | def test_no_websiteKey(self): 57 | with pytest.raises(TypeError): 58 | MTCaptcha( 59 | rucaptcha_key=self.RUCAPTCHA_KEY, 60 | websiteURL=self.websiteURL, 61 | ) 62 | 63 | def test_wrong_method(self): 64 | with pytest.raises(ValueError): 65 | MTCaptcha( 66 | rucaptcha_key=self.RUCAPTCHA_KEY, 67 | websiteURL=self.websiteURL, 68 | websiteKey=self.websiteKey, 69 | method=self.get_random_string(length=5), 70 | ) 71 | -------------------------------------------------------------------------------- /tests/test_recaptcha.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import ReCaptchaEnm 5 | from python_rucaptcha.re_captcha import ReCaptcha 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestReCaptcha(BaseTest): 10 | googlekey = "6LeIxboZAAAAAFQy7d8GPzgRZu2bV0GwKS8ue_cH" 11 | pageurl = "https://rucaptcha.com/demo/recaptcha-v2" 12 | kwargs_params = { 13 | "recaptchaDataSValue": "sample-recaptchaDataSValue", 14 | "isInvisible": True, 15 | "userAgent": "Some specific user agent", 16 | "proxyType": "socks5", 17 | "proxyAddress": BaseTest.proxyAddress, 18 | "proxyPort": BaseTest.proxyPort, 19 | } 20 | 21 | @pytest.mark.parametrize("method", ReCaptchaEnm.list_values()) 22 | def test_args(self, method: str): 23 | instance = ReCaptcha( 24 | rucaptcha_key=self.RUCAPTCHA_KEY, 25 | websiteURL=self.pageurl, 26 | websiteKey=self.googlekey, 27 | method=method, 28 | ) 29 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 30 | assert instance.create_task_payload["task"]["type"] == method 31 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 32 | assert instance.create_task_payload["task"]["websiteKey"] == self.googlekey 33 | 34 | def test_kwargs(self): 35 | instance = ReCaptcha( 36 | rucaptcha_key=self.RUCAPTCHA_KEY, 37 | websiteURL=self.pageurl, 38 | websiteKey=self.googlekey, 39 | method=ReCaptchaEnm.RecaptchaV2TaskProxyless, 40 | **self.kwargs_params, 41 | ) 42 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 43 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 44 | 45 | """ 46 | Success tests 47 | """ 48 | 49 | def test_methods_exists(self): 50 | assert "captcha_handler" in ReCaptcha.__dict__.keys() 51 | assert "aio_captcha_handler" in ReCaptcha.__dict__.keys() 52 | 53 | def test_basic_data(self): 54 | instance = ReCaptcha( 55 | rucaptcha_key=self.RUCAPTCHA_KEY, 56 | websiteURL=self.pageurl, 57 | websiteKey=self.googlekey, 58 | method=ReCaptchaEnm.RecaptchaV2TaskProxyless.value, 59 | ) 60 | 61 | result = instance.captcha_handler() 62 | 63 | assert isinstance(result, dict) is True 64 | if not result["errorId"]: 65 | assert result["status"] == "ready" 66 | assert isinstance(result["solution"], dict) is True 67 | assert isinstance(result["taskId"], int) is True 68 | else: 69 | assert result["errorId"] in (1, 12) 70 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 71 | 72 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 73 | 74 | async def test_aio_basic_data(self): 75 | instance = ReCaptcha( 76 | rucaptcha_key=self.RUCAPTCHA_KEY, 77 | websiteURL=self.pageurl, 78 | websiteKey=self.googlekey, 79 | method=ReCaptchaEnm.RecaptchaV2TaskProxyless.value, 80 | ) 81 | 82 | result = await instance.aio_captcha_handler() 83 | 84 | assert isinstance(result, dict) is True 85 | if not result["errorId"]: 86 | assert result["status"] == "ready" 87 | assert isinstance(result["solution"], dict) is True 88 | assert isinstance(result["taskId"], int) is True 89 | else: 90 | assert result["errorId"] in (1, 12) 91 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 92 | 93 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 94 | 95 | def test_context_basic_data(self): 96 | with ReCaptcha( 97 | rucaptcha_key=self.RUCAPTCHA_KEY, 98 | websiteURL=self.pageurl, 99 | websiteKey=self.googlekey, 100 | method=ReCaptchaEnm.RecaptchaV2TaskProxyless.value, 101 | ) as instance: 102 | assert instance 103 | 104 | async def test_context_aio_basic_data(self): 105 | async with ReCaptcha( 106 | rucaptcha_key=self.RUCAPTCHA_KEY, 107 | websiteURL=self.pageurl, 108 | websiteKey=self.googlekey, 109 | method=ReCaptchaEnm.RecaptchaV2TaskProxyless.value, 110 | ) as instance: 111 | assert instance 112 | 113 | """ 114 | Fail tests 115 | """ 116 | 117 | def test_wrong_method(self): 118 | with pytest.raises(ValueError): 119 | ReCaptcha( 120 | rucaptcha_key=self.RUCAPTCHA_KEY, 121 | websiteURL=self.pageurl, 122 | websiteKey=self.googlekey, 123 | method=self.get_random_string(length=5), 124 | ) 125 | -------------------------------------------------------------------------------- /tests/test_tencent.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.tencent import Tencent 5 | from python_rucaptcha.core.enums import TencentEnm 6 | 7 | 8 | class TestTencent(BaseTest): 9 | websiteURL = "https://www.tencentcloud.com/account/register" 10 | appId = "2009899766" 11 | 12 | kwargs_params = { 13 | "proxyLogin": "user23", 14 | "proxyPassword": "p4$$w0rd", 15 | "proxyType": "socks5", 16 | "proxyAddress": BaseTest.proxyAddress, 17 | "proxyPort": BaseTest.proxyPort, 18 | } 19 | 20 | def test_methods_exists(self): 21 | assert "captcha_handler" in Tencent.__dict__.keys() 22 | assert "aio_captcha_handler" in Tencent.__dict__.keys() 23 | 24 | @pytest.mark.parametrize("method", TencentEnm.list_values()) 25 | def test_args(self, method: str): 26 | instance = Tencent( 27 | rucaptcha_key=self.RUCAPTCHA_KEY, 28 | websiteURL=self.websiteURL, 29 | appId=self.appId, 30 | method=method, 31 | ) 32 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 33 | assert instance.create_task_payload["task"]["type"] == method 34 | assert instance.create_task_payload["task"]["websiteURL"] == self.websiteURL 35 | assert instance.create_task_payload["task"]["appId"] == self.appId 36 | 37 | @pytest.mark.parametrize("method", TencentEnm.list_values()) 38 | def test_kwargs(self, method: str): 39 | instance = Tencent( 40 | rucaptcha_key=self.RUCAPTCHA_KEY, 41 | websiteURL=self.websiteURL, 42 | appId=self.appId, 43 | method=method, 44 | **self.kwargs_params, 45 | ) 46 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 47 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 48 | 49 | """ 50 | Fail tests 51 | """ 52 | 53 | @pytest.mark.parametrize("method", TencentEnm.list_values()) 54 | def test_no_websiteURL(self, method: str): 55 | with pytest.raises(TypeError): 56 | Tencent(rucaptcha_key=self.RUCAPTCHA_KEY, appId=self.appId, method=method) 57 | 58 | @pytest.mark.parametrize("method", TencentEnm.list_values()) 59 | def test_no_appId(self, method: str): 60 | with pytest.raises(TypeError): 61 | Tencent(rucaptcha_key=self.RUCAPTCHA_KEY, websiteURL=self.websiteURL, method=method) 62 | 63 | @pytest.mark.parametrize("method", TencentEnm.list_values()) 64 | def test_wrong_method(self, method: str): 65 | with pytest.raises(ValueError): 66 | Tencent( 67 | rucaptcha_key=self.RUCAPTCHA_KEY, 68 | websiteURL=self.websiteURL, 69 | appId=self.appId, 70 | method=self.get_random_string(length=5), 71 | ) 72 | -------------------------------------------------------------------------------- /tests/test_text.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.core.enums import TextCaptchaEnm 5 | from python_rucaptcha.text_captcha import TextCaptcha 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestTextCaptcha(BaseTest): 10 | questions = (("en", "Our planet name?"), ("rn", "Название нашей планеты?")) 11 | 12 | """ 13 | Success tests 14 | """ 15 | 16 | def test_methods_exists(self): 17 | assert "captcha_handler" in TextCaptcha.__dict__.keys() 18 | assert "aio_captcha_handler" in TextCaptcha.__dict__.keys() 19 | 20 | def test_args(self): 21 | instance = TextCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY) 22 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 23 | assert instance.create_task_payload["task"]["type"] == TextCaptchaEnm.TextCaptchaTask 24 | 25 | @pytest.mark.parametrize("lang_code, question", questions) 26 | def test_basic(self, lang_code: str, question: str): 27 | instance = TextCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, languagePool=lang_code) 28 | 29 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 30 | 31 | result = instance.captcha_handler(textcaptcha=question) 32 | 33 | assert isinstance(result, dict) is True 34 | 35 | if not result["errorId"]: 36 | assert result["status"] == "ready" 37 | assert isinstance(result["solution"]["text"], str) is True 38 | assert isinstance(result["taskId"], int) is True 39 | else: 40 | assert result["errorId"] in (1, 12) 41 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 42 | 43 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 44 | 45 | @pytest.mark.parametrize("lang_code, question", questions) 46 | async def test_aio_basic(self, lang_code, question): 47 | instance = TextCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY, languagePool=lang_code) 48 | 49 | result = await instance.aio_captcha_handler(textcaptcha=question) 50 | 51 | assert isinstance(result, dict) is True 52 | 53 | if not result["errorId"]: 54 | assert result["status"] == "ready" 55 | assert isinstance(result["solution"]["text"], str) is True 56 | assert isinstance(result["taskId"], int) is True 57 | else: 58 | assert result["errorId"] in (1, 12) 59 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 60 | 61 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 62 | 63 | """ 64 | Fail tests 65 | """ 66 | 67 | def test_no_textcaptcha(self): 68 | with pytest.raises(TypeError): 69 | TextCaptcha(rucaptcha_key=self.RUCAPTCHA_KEY).captcha_handler() 70 | -------------------------------------------------------------------------------- /tests/test_turnstile.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tests.conftest import BaseTest 4 | from python_rucaptcha.turnstile import Turnstile 5 | from python_rucaptcha.core.enums import TurnstileCaptchaEnm 6 | from python_rucaptcha.core.serializer import GetTaskResultResponseSer 7 | 8 | 9 | class TestTurnstile(BaseTest): 10 | pageurl = "https://rucaptcha.com/demo/cloudflare-turnstile" 11 | sitekey = "0x4AAAAAAAC3DHQFLr1GavRN" 12 | useragent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36" 13 | kwargs_params = { 14 | "proxyType": "socks5", 15 | "proxyAddress": BaseTest.proxyAddress, 16 | "proxyPort": BaseTest.proxyPort, 17 | } 18 | 19 | def test_methods_exists(self): 20 | assert "captcha_handler" in Turnstile.__dict__.keys() 21 | assert "aio_captcha_handler" in Turnstile.__dict__.keys() 22 | 23 | @pytest.mark.parametrize("method", TurnstileCaptchaEnm.list_values()) 24 | def test_args(self, method: str): 25 | instance = Turnstile( 26 | rucaptcha_key=self.RUCAPTCHA_KEY, 27 | websiteURL=self.pageurl, 28 | websiteKey=self.sitekey, 29 | userAgent=self.useragent, 30 | method=method, 31 | ) 32 | assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY 33 | assert instance.create_task_payload["task"]["type"] == method 34 | assert instance.create_task_payload["task"]["websiteURL"] == self.pageurl 35 | assert instance.create_task_payload["task"]["websiteKey"] == self.sitekey 36 | assert instance.create_task_payload["task"]["userAgent"] == self.useragent 37 | 38 | def test_kwargs(self): 39 | instance = Turnstile( 40 | rucaptcha_key=self.RUCAPTCHA_KEY, 41 | websiteURL=self.pageurl, 42 | websiteKey=self.sitekey, 43 | userAgent=self.useragent, 44 | method=TurnstileCaptchaEnm.TurnstileTaskProxyless, 45 | **self.kwargs_params, 46 | ) 47 | assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) 48 | assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) 49 | 50 | """ 51 | Success tests 52 | """ 53 | 54 | def test_basic_data(self): 55 | instance = Turnstile( 56 | rucaptcha_key=self.RUCAPTCHA_KEY, 57 | websiteURL=self.pageurl, 58 | websiteKey=self.sitekey, 59 | userAgent=self.useragent, 60 | method=TurnstileCaptchaEnm.TurnstileTaskProxyless.value, 61 | ) 62 | 63 | result = instance.captcha_handler() 64 | 65 | assert isinstance(result, dict) is True 66 | if not result["errorId"]: 67 | assert result["status"] == "ready" 68 | assert isinstance(result["solution"], dict) is True 69 | assert isinstance(result["taskId"], int) is True 70 | else: 71 | assert result["errorId"] in (1, 12) 72 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 73 | 74 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 75 | 76 | async def test_aio_basic_data(self): 77 | instance = Turnstile( 78 | rucaptcha_key=self.RUCAPTCHA_KEY, 79 | websiteURL=self.pageurl, 80 | websiteKey=self.sitekey, 81 | userAgent=self.useragent, 82 | method=TurnstileCaptchaEnm.TurnstileTaskProxyless.value, 83 | ) 84 | 85 | result = await instance.aio_captcha_handler() 86 | 87 | assert isinstance(result, dict) is True 88 | if not result["errorId"]: 89 | assert result["status"] == "ready" 90 | assert isinstance(result["solution"], dict) is True 91 | assert isinstance(result["taskId"], int) is True 92 | else: 93 | assert result["errorId"] in (1, 12) 94 | assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" 95 | 96 | assert result.keys() == GetTaskResultResponseSer().to_dict().keys() 97 | 98 | def test_context_basic_data(self): 99 | with Turnstile( 100 | rucaptcha_key=self.RUCAPTCHA_KEY, 101 | websiteURL=self.pageurl, 102 | websiteKey=self.sitekey, 103 | userAgent=self.useragent, 104 | method=TurnstileCaptchaEnm.TurnstileTaskProxyless.value, 105 | ) as instance: 106 | assert instance 107 | 108 | async def test_context_aio_basic_data(self): 109 | async with Turnstile( 110 | rucaptcha_key=self.RUCAPTCHA_KEY, 111 | websiteURL=self.pageurl, 112 | websiteKey=self.sitekey, 113 | userAgent=self.useragent, 114 | method=TurnstileCaptchaEnm.TurnstileTaskProxyless.value, 115 | ) as instance: 116 | assert instance 117 | 118 | """ 119 | Fail tests 120 | """ 121 | 122 | def test_wrong_method(self): 123 | with pytest.raises(ValueError): 124 | Turnstile( 125 | rucaptcha_key=self.RUCAPTCHA_KEY, 126 | websiteURL=self.pageurl, 127 | websiteKey=self.sitekey, 128 | userAgent=self.useragent, 129 | method=self.get_random_string(5), 130 | ) 131 | --------------------------------------------------------------------------------