├── .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 | [](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 | [](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=python-rucaptcha)
8 |
9 |
10 |
11 | [](https://badge.fury.io/py/python-rucaptcha)
12 | [](https://badge.fury.io/py/python-rucaptcha)
13 | [](https://pepy.tech/project/python-rucaptcha)
14 | [](https://andreidrang.github.io/python-rucaptcha/)
15 |
16 | [](https://codeclimate.com/github/AndreiDrang/python-rucaptcha/maintainability)
17 | [](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 | [](https://codecov.io/gh/AndreiDrang/python-rucaptcha)
19 |
20 | [](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/sphinx.yml)
21 | [](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/build.yml)
22 | [](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/install.yml)
23 | [](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/test.yml)
24 | [](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: [](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 | 
5 |
6 | ### [Capsolver](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=python-rucaptcha)
7 |
8 | [](https://www.capsolver.com/?utm_source=github&utm_medium=repo&utm_campaign=scraping&utm_term=python-rucaptcha)
9 |
10 |
11 |
12 | [](https://badge.fury.io/py/python-rucaptcha)
13 | [](https://badge.fury.io/py/python-rucaptcha)
14 | [](https://pepy.tech/project/python-rucaptcha)
15 | [](https://andreidrang.github.io/python-rucaptcha/)
16 |
17 | [](https://codeclimate.com/github/AndreiDrang/python-rucaptcha/maintainability)
18 | [](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 | [](https://codecov.io/gh/AndreiDrang/python-rucaptcha)
20 |
21 | [](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/build.yml)
22 | [](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/install.yml)
23 | [](https://github.com/AndreiDrang/python-rucaptcha/actions/workflows/test.yml)
24 | [](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 |
--------------------------------------------------------------------------------