Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

6.0.1 - CutCaptcha added #190

Merged
merged 17 commits into from
Dec 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ lint:
isort src/ --check-only

upload:
pip3 install twine
pip3 install twine wheel
cd src/ && python setup.py upload

tests: install
Expand Down
Binary file modified docs/_static/RuCaptchaMedium.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Check our other projects here - `RedPandaDev group <https://red-panda-dev.xyz/bl
modules/turnstile/example.rst
modules/image/example.rst
modules/audio/example.rst
modules/cut-captcha/example.rst
modules/control/example.rst

.. toctree::
Expand Down
12 changes: 12 additions & 0 deletions docs/modules/cut-captcha/example.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CutCaptcha
==========

To import this module:

.. code-block:: python

from python_rucaptcha.cutcaptcha import CutCaptcha


.. autoclass:: python_rucaptcha.cutcaptcha.CutCaptcha
:members:
4 changes: 4 additions & 0 deletions docs/modules/enum/info.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,7 @@ To import this module:
.. autoclass:: python_rucaptcha.core.enums.AmazonWAFCaptchaEnm
:members:
:undoc-members:

.. autoclass:: python_rucaptcha.core.enums.CutCaptchaEnm
:members:
:undoc-members:
4 changes: 2 additions & 2 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ sphinx==7.2.6
pallets_sphinx_themes==2.1.1
myst-parser==2.0.0
autodoc_pydantic==2.0.1
pydantic==2.4.2
pydantic-settings==2.0.3
pydantic==2.5.2
pydantic-settings==2.1.0
Binary file modified files/RuCaptchaHigh.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 26 additions & 25 deletions files/drawing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/python_rucaptcha/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "6.0"
__version__ = "6.0.1"
21 changes: 13 additions & 8 deletions src/python_rucaptcha/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
import uuid
import base64
import asyncio
from typing import Union, Optional
from typing import Optional
from pathlib import Path

import aiohttp
import requests
from requests.adapters import HTTPAdapter

from . import enums
from .enums import SaveFormatsEnm
from .enums import ServiceEnm, SaveFormatsEnm
from .config import RETRIES, ASYNC_RETRIES
from .serializer import TaskSer, CaptchaOptionsSer, CreateTaskBaseSer, GetTaskResultRequestSer, GetTaskResultResponseSer
from .result_handler import get_sync_result, get_async_result
Expand All @@ -25,7 +24,7 @@ def __init__(
rucaptcha_key: str,
method: str,
sleep_time: int = 10,
service_type: str = enums.ServiceEnm.TWOCAPTCHA.value,
service_type: str = ServiceEnm.TWOCAPTCHA.value,
**kwargs,
):
"""
Expand Down Expand Up @@ -56,7 +55,7 @@ def __init__(
self.session.mount("http://", HTTPAdapter(max_retries=RETRIES))
self.session.mount("https://", HTTPAdapter(max_retries=RETRIES))

def _processing_response(self, **kwargs: dict) -> Union[dict, Exception]:
def _processing_response(self, **kwargs: dict) -> dict:
"""
Method processing captcha solving task creation result
:param kwargs: additional params for Requests library
Expand All @@ -71,7 +70,10 @@ def _processing_response(self, **kwargs: dict) -> Union[dict, Exception]:
else:
return response.to_dict()
except Exception as error:
return error
self.result.errorId = 12
self.result.errorCode = self.NO_CAPTCHA_ERR
self.result.errorDescription = str(error)
return self.result.to_dict()

# wait captcha solving
time.sleep(self.params.sleep_time)
Expand All @@ -96,7 +98,7 @@ async def aio_url_read(self, url: str, **kwargs) -> bytes:
async with session.get(url=url, **kwargs) as resp:
return await resp.content.read()

async def _aio_processing_response(self) -> Union[dict, Exception]:
async def _aio_processing_response(self) -> dict:
"""
Method processing async captcha solving task creation result
"""
Expand All @@ -109,7 +111,10 @@ async def _aio_processing_response(self) -> Union[dict, Exception]:
else:
return response.to_dict()
except Exception as error:
return error
self.result.errorId = 12
self.result.errorCode = self.NO_CAPTCHA_ERR
self.result.errorDescription = str(error)
return self.result.to_dict()

# wait captcha solving
await asyncio.sleep(self.params.sleep_time)
Expand Down
5 changes: 5 additions & 0 deletions src/python_rucaptcha/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,8 @@ class TextCaptchaEnm(str, MyEnum):

class AudioCaptchaEnm(str, MyEnum):
AudioTask = "AudioTask"


class CutCaptchaEnm(str, MyEnum):
CutCaptchaTask = "CutCaptchaTask"
CutCaptchaTaskProxyless = "CutCaptchaTaskProxyless"
116 changes: 116 additions & 0 deletions src/python_rucaptcha/cutcaptcha.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
from typing import Union

from .core.base import BaseCaptcha
from .core.enums import CutCaptchaEnm


class CutCaptcha(BaseCaptcha):
def __init__(
self,
websiteURL: str,
miseryKey: str,
apiKey: str,
method: Union[str, CutCaptchaEnm] = CutCaptchaEnm.CutCaptchaTaskProxyless,
*args,
**kwargs,
):
"""
The class is used to work with CutCaptcha.

Args:
rucaptcha_key: User API key
websiteURL: Full URL of the captcha page
miseryKey: The value of CUTCAPTCHA_MISERY_KEY variable defined on page.
apiKey: The value of data-apikey attribute of iframe's body.
Also the name of javascript file included on the page
method: Captcha type
kwargs: Not required params for task creation request

Examples:
>>> CutCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
... websiteURL="https://example.cc/foo/bar.html",
... miseryKey="a1488b66da00bf332a1488993a5443c79047e752",
... apiKey="SAb83IIB",
... method=CutCaptchaEnm.CutCaptchaTaskProxyless
... ).captcha_handler()
{
"errorId":0,
"status":"ready",
"solution":{
"token":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A",
"respKey":"E0_eyJ0eXAiOiJK...y2w5_YbP8PGuJBBo",
"userAgent":"Mozilla/5.0 (.......",
"gRecaptchaResponse":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A"
},
"cost":"0.00299",
"ip":"1.2.3.4",
"createTime":1692863536,
"endTime":1692863556,
"solveCount":1,
"taskId": 73243152973,
}

>>> await CutCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
... websiteURL="https://example.cc/foo/bar.html",
... miseryKey="a1488b66da00bf332a1488993a5443c79047e752",
... apiKey="SAb83IIB",
... method=CutCaptchaEnm.CutCaptchaTaskProxyless
... ).aio_captcha_handler()
{
"errorId":0,
"status":"ready",
"solution":{
"token":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A",
"respKey":"E0_eyJ0eXAiOiJK...y2w5_YbP8PGuJBBo",
"userAgent":"Mozilla/5.0 (........",
"gRecaptchaResponse":"P1_eyJ0eXAiOiJKV...1LDq89KyJ5A"
},
"cost":"0.00299",
"ip":"1.2.3.4",
"createTime":1692863536,
"endTime":1692863556,
"solveCount":1,
"taskId": 73243152973,
}

Returns:
Dict with full server response

Notes:
https://2captcha.com/api-docs/cutcaptcha
"""
super().__init__(method=method, *args, **kwargs)

self.create_task_payload["task"].update({"websiteURL": websiteURL, "miseryKey": miseryKey, "apiKey": apiKey})

# check user params
if method not in CutCaptchaEnm.list_values():
raise ValueError(f"Invalid method parameter set, available - {CutCaptchaEnm.list_values()}")

def captcha_handler(self, **kwargs) -> dict:
"""
Sync solving method

Args:
kwargs: Parameters for the `requests` library

Returns:
Dict with full server response

Notes:
Check class docstirng for more info
"""

return self._processing_response(**kwargs)

async def aio_captcha_handler(self) -> dict:
"""
Async solving method

Returns:
Dict with full server response

Notes:
Check class docstirng for more info
"""
return await self._aio_processing_response()
2 changes: 1 addition & 1 deletion src/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

# Package meta-data.
NAME = "python-rucaptcha"
DESCRIPTION = "Python 3.7+ RuCaptcha library with AIO module."
DESCRIPTION = "Python 3.9+ RuCaptcha library with AIO module."
URL = "https://andreidrang.github.io/python-rucaptcha/"
EMAIL = "[email protected]"
AUTHOR = "AndreiDrang, redV0ID"
Expand Down
11 changes: 10 additions & 1 deletion tests/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from tests.conftest import BaseTest
from python_rucaptcha.core.base import BaseCaptcha
from python_rucaptcha.core.enums import MyEnum, ControlEnm
from python_rucaptcha.core.enums import MyEnum, ControlEnm, ServiceEnm
from python_rucaptcha.core.config import RETRIES, ASYNC_RETRIES, attempts_generator


Expand Down Expand Up @@ -104,3 +104,12 @@ def test_attempts_generator(self):
for attempt in attempts:
assert isinstance(attempt, int)
assert attempt == 4


class TestDeathbycaptcha(BaseTest):
def test_attempts_generator(self):
BaseCaptcha(
rucaptcha_key=self.RUCAPTCHA_KEY,
service_type=ServiceEnm.DEATHBYCAPTCHA.value,
method=ControlEnm.control.value,
)
Loading
Loading