Skip to content

Commit

Permalink
Merge pull request #190 from AndreiDrang/master
Browse files Browse the repository at this point in the history
6.0.1 - CutCaptcha added
  • Loading branch information
AndreiDrang authored Dec 8, 2023
2 parents e4fae1e + 8d58e2d commit 1988994
Show file tree
Hide file tree
Showing 15 changed files with 350 additions and 39 deletions.
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 = "python-captcha@pm.me"
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

0 comments on commit 1988994

Please sign in to comment.