diff --git a/.gitignore b/.gitignore index bad8eb9a..cec94e62 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ env/ /src/coverage/lcov.info /docs/_build/ /coverage/ +.osgrep diff --git a/README.md b/README.md index 3c0831a2..6edea8e2 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,7 @@ token = asyncio.run(solve()) | KeyCaptcha | `KeyCaptcha` | KeyCAPTCHA service | | Amazon WAF | `AmazonWaf` | AWS WAF challenge | | ALTCHA | `AltchaCaptcha` | ALTCHA challenge | +| Binance | `BinanceCaptcha` | Token-based Binance challenge | | Grid | `GridCaptcha` | Select grid cells | | Coordinates | `CoordinatesCaptcha` | Click on coordinates | | And 20+ more | ... | See [full docs](https://andreidrang.github.io/python-rucaptcha/) | diff --git a/docs/conf.py b/docs/conf.py index 8d3f987a..cb276aa4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,6 +26,7 @@ image_captcha, lemin_captcha, rotate_captcha, + binance_captcha, datadome_captcha, friendly_captcha, cyber_siara_captcha, diff --git a/docs/index.rst b/docs/index.rst index 586851dd..fd9e801c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -52,6 +52,7 @@ Check our other projects here - `RedPandaDev group =2.32.4 # not directly required, pinned by Snyk to avoid a vulnerability -urllib3>=2.6.3 # not directly required, pinned by Snyk to avoid a vulnerability +urllib3>=2.2.2 # not directly required, pinned by Snyk to avoid a vulnerability zipp>=3.23.1 # not directly required, pinned by Snyk to avoid a vulnerability +idna>=3.15 # not directly required, pinned by Snyk to avoid a vulnerability diff --git a/pyproject.toml b/pyproject.toml index 4ae1e060..24ff62cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -69,7 +69,8 @@ keywords = [ "captcha", "vk-captcha", "fox-captcha", "temu-captcha", - "friendly-captcha" + "friendly-captcha", + "binance-captcha" ] license = "MIT" classifiers = [ diff --git a/src/python_rucaptcha/binance_captcha.py b/src/python_rucaptcha/binance_captcha.py new file mode 100644 index 00000000..d925b11b --- /dev/null +++ b/src/python_rucaptcha/binance_captcha.py @@ -0,0 +1,155 @@ +from typing import Union, Optional + +from .core.base import BaseCaptcha +from .core.enums import BinanceCaptchaEnm + + +class BinanceCaptcha(BaseCaptcha): + def __init__( + self, + websiteURL: str, + websiteKey: str, + validateId: str, + method: Union[str, BinanceCaptchaEnm] = BinanceCaptchaEnm.BinanceTaskProxyless, + userAgent: Optional[str] = None, + proxyType: Optional[str] = None, + proxyAddress: Optional[str] = None, + proxyPort: Optional[int] = None, + proxyLogin: Optional[str] = None, + proxyPassword: Optional[str] = None, + *args, + **kwargs, + ): + """ + The class is used to work with Binance CAPTCHA. + + Args: + rucaptcha_key: User API key + websiteURL: Full URL of the page where the captcha is loaded + websiteKey: Value of bizId, bizType, or bizCode from page requests + validateId: Dynamic value of validateId, securityId, or securityCheckResponseValidateId + method: Captcha type + userAgent: User-Agent string to be used when solving the captcha + proxyType: Proxy type (http, https, socks4, socks5) + proxyAddress: Proxy IP address or hostname + proxyPort: Proxy port + proxyLogin: Proxy login + proxyPassword: Proxy password + + Examples: + >>> BinanceCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", + ... websiteURL="https://example.com/page-with-binance", + ... websiteKey="login", + ... validateId="cb0bfefa598...e54ecd57b", + ... userAgent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + ... "(KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36", + ... method=BinanceCaptchaEnm.BinanceTaskProxyless.value, + ... ).captcha_handler() + { + "errorId":0, + "status":"ready", + "solution":{ + "token":"captcha#09ba4905a79f44f...kc99maS943qIsquNP9D77", + "userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" + }, + "cost":"0.00299", + "ip":"1.2.3.4", + "createTime":1692863536, + "endTime":1692863556, + "solveCount":1, + "taskId": 73243152973, + } + + >>> await BinanceCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", + ... websiteURL="https://example.com/page-with-binance", + ... websiteKey="login", + ... validateId="cb0bfefa598...e54ecd57b", + ... method=BinanceCaptchaEnm.BinanceTask.value, + ... proxyType="http", + ... proxyAddress="1.2.3.4", + ... proxyPort=8080, + ... ).aio_captcha_handler() + { + "errorId":0, + "status":"ready", + "solution":{ + "token":"captcha#09ba4905a79f44f...kc99maS943qIsquNP9D77", + "userAgent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" + }, + "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/binance-captcha + + https://rucaptcha.com/api-docs/binance-captcha + """ + super().__init__(method=method, *args, **kwargs) + + # Validate method + if method not in BinanceCaptchaEnm.list_values(): + raise ValueError(f"Invalid method parameter set, available - {BinanceCaptchaEnm.list_values()}") + + # Build task payload + task_data = { + "websiteURL": websiteURL, + "websiteKey": websiteKey, + "validateId": validateId, + } + + if userAgent: + task_data["userAgent"] = userAgent + + # Add proxy params only for non-proxyless methods + if method == BinanceCaptchaEnm.BinanceTask.value: + if not all([proxyType, proxyAddress, proxyPort]): + raise ValueError( + "Proxy parameters (proxyType, proxyAddress, proxyPort) are required for BinanceTask" + ) + task_data.update( + { + "proxyType": proxyType, + "proxyAddress": proxyAddress, + "proxyPort": proxyPort, + } + ) + if proxyLogin and proxyPassword: + task_data["proxyLogin"] = proxyLogin + task_data["proxyPassword"] = proxyPassword + + self.create_task_payload["task"].update(task_data) + + 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() diff --git a/src/python_rucaptcha/core/enums.py b/src/python_rucaptcha/core/enums.py index ba19ba23..c6bd556f 100644 --- a/src/python_rucaptcha/core/enums.py +++ b/src/python_rucaptcha/core/enums.py @@ -177,7 +177,13 @@ class CaptchaFoxEnm(str, MyEnum): class VKCaptchaEnm(str, MyEnum): VKCaptchaTask = "VKCaptchaTask" + VKCaptchaImageTask = "VKCaptchaImageTask" class TemuCaptchaEnm(str, MyEnum): TemuCaptchaTask = "TemuCaptchaTask" + + +class BinanceCaptchaEnm(str, MyEnum): + BinanceTaskProxyless = "BinanceTaskProxyless" + BinanceTask = "BinanceTask" diff --git a/src/python_rucaptcha/vk_captcha.py b/src/python_rucaptcha/vk_captcha.py index 23d9f6a8..ff76f763 100644 --- a/src/python_rucaptcha/vk_captcha.py +++ b/src/python_rucaptcha/vk_captcha.py @@ -1,32 +1,43 @@ -from typing import Any +from typing import Any, Union, Optional from .core.base import BaseCaptcha -from .core.enums import VKCaptchaEnm +from .core.enums import VKCaptchaEnm, SaveFormatsEnm class VKCaptcha(BaseCaptcha): def __init__( self, - redirectUri: str, - userAgent: str, - proxyType: str, - proxyAddress: str, - proxyPort: str, + method: Union[str, VKCaptchaEnm] = VKCaptchaEnm.VKCaptchaTask, + redirectUri: Optional[str] = None, + userAgent: Optional[str] = None, + proxyType: Optional[str] = None, + proxyAddress: Optional[str] = None, + proxyPort: Optional[int] = None, + proxyLogin: Optional[str] = None, + proxyPassword: Optional[str] = None, + save_format: Union[str, SaveFormatsEnm] = SaveFormatsEnm.TEMP, + img_path: str = "PythonRuCaptchaImages", *args, - **kwargs: dict[str, Any], + **kwargs, ): """ - The class is used to work with VKCaptchaTask. + The class is used to work with VKCaptchaTask and VKCaptchaImageTask. + + VKCaptchaTask requires proxy and returns a token. + VKCaptchaImageTask takes a captcha image and steps, returns image solution. Args: rucaptcha_key: User API key - redirectUri: The URL that is returned on requests to the captcha API. - userAgent: User-Agent of your browser will be used to load the captcha. - Use only modern browser's User-Agents - proxyType: Proxy type - `http`, `socks4`, `socks5` - proxyAddress: Proxy IP address or hostname - proxyPort: Proxy port - method: Captcha type + method: Captcha type - VKCaptchaTask or VKCaptchaImageTask + redirectUri: The URL that is returned on requests to the captcha API (VKCaptchaTask) + userAgent: User-Agent of your browser will be used to load the captcha (VKCaptchaTask) + proxyType: Proxy type - http, https, socks5 (VKCaptchaTask) + proxyAddress: Proxy IP address or hostname (VKCaptchaTask) + proxyPort: Proxy port (VKCaptchaTask) + proxyLogin: Proxy login (VKCaptchaTask) + proxyPassword: Proxy password (VKCaptchaTask) + save_format: Image save format for VKCaptchaImageTask - 'temp' or 'const' + img_path: Folder to save captcha images for VKCaptchaImageTask kwargs: Not required params for task creation request Examples: @@ -35,7 +46,7 @@ def __init__( ... userAgent="Mozilla/5.0 .....", ... proxyType="socks5", ... proxyAddress="1.2.3.4", - ... proxyPort="445", + ... proxyPort=445, ... ).captcha_handler() { "errorId":0, @@ -56,7 +67,7 @@ def __init__( ... userAgent="Mozilla/5.0 .....", ... proxyType="socks5", ... proxyAddress="1.2.3.4", - ... proxyPort="445", + ... proxyPort=445, ... ).aio_captcha_handler() { "errorId":0, @@ -72,6 +83,28 @@ def __init__( "taskId": 73243152973, } + >>> VKCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122", + ... method=VKCaptchaEnm.VKCaptchaImageTask, + ... ).captcha_handler( + ... captcha_link="https://example.com/vk_captcha.png", steps=[3, 4, 5] + ... ) + { + "errorId":0, + "status":"ready", + "solution":{ + "best_step": 1, + "preview": "...", + "solution": "...", + "answer": "..." + }, + "cost":"0.002", + "ip":"1.2.3.4", + "createTime":1692863536, + "endTime":1692863556, + "solveCount":0, + "taskId": 73243152973, + } + Returns: Dict with full server response @@ -80,23 +113,51 @@ def __init__( https://rucaptcha.com/api-docs/vk-captcha """ - super().__init__(method=VKCaptchaEnm.VKCaptchaTask, *args, **kwargs) + super().__init__(method=method, *args, **kwargs) - self.create_task_payload["task"].update( - { - "websiteURL": redirectUri, + if method not in VKCaptchaEnm.list_values(): + raise ValueError(f"Invalid method parameter set, available - {VKCaptchaEnm.list_values()}") + + self.method = method + self.save_format = save_format + self.img_path = img_path + + if method == VKCaptchaEnm.VKCaptchaTask: + if not all([redirectUri, userAgent, proxyType, proxyAddress, proxyPort]): + raise ValueError( + "redirectUri, userAgent, proxyType, proxyAddress, " + "and proxyPort are required for VKCaptchaTask" + ) + + task_data = { + "redirectUri": redirectUri, "userAgent": userAgent, "proxyType": proxyType, "proxyAddress": proxyAddress, "proxyPort": proxyPort, } - ) + if proxyLogin and proxyPassword: + task_data["proxyLogin"] = proxyLogin + task_data["proxyPassword"] = proxyPassword + + self.create_task_payload["task"].update(task_data) - def captcha_handler(self, **kwargs: dict[str, Any]) -> dict[str, Any]: + def captcha_handler( + self, + captcha_link: Optional[str] = None, + captcha_file: Optional[str] = None, + captcha_base64: Optional[bytes] = None, + steps: Optional[list[int]] = None, + **kwargs: dict[str, Any], + ) -> dict[str, Any]: """ Sync solving method Args: + captcha_link: Captcha image URL (VKCaptchaImageTask) + captcha_file: Captcha image file path (VKCaptchaImageTask) + captcha_base64: Captcha image BASE64 info (VKCaptchaImageTask) + steps: List of step values for VKCaptchaImageTask kwargs: additional params for `requests` library Returns: @@ -105,16 +166,60 @@ def captcha_handler(self, **kwargs: dict[str, Any]) -> dict[str, Any]: Notes: Check class docstirng for more info """ + if self.method == VKCaptchaEnm.VKCaptchaImageTask: + self._body_file_processing( + save_format=self.save_format, + file_path=self.img_path, + image_key="image", + captcha_link=captcha_link, + captcha_file=captcha_file, + captcha_base64=captcha_base64, + ) + if steps: + self.create_task_payload["task"]["steps"] = steps + if not self.result.errorId: + return self._processing_response(**kwargs) + return self.result.to_dict() + return self._processing_response(**kwargs) - async def aio_captcha_handler(self) -> dict[str, Any]: + async def aio_captcha_handler( + self, + captcha_link: Optional[str] = None, + captcha_file: Optional[str] = None, + captcha_base64: Optional[bytes] = None, + steps: Optional[list[int]] = None, + **kwargs: dict[str, Any], + ) -> dict[str, Any]: """ Async solving method + Args: + captcha_link: Captcha image URL (VKCaptchaImageTask) + captcha_file: Captcha image file path (VKCaptchaImageTask) + captcha_base64: Captcha image BASE64 info (VKCaptchaImageTask) + steps: List of step values for VKCaptchaImageTask + kwargs: additional params for `aiohttp` library + Returns: Dict with full server response Notes: Check class docstirng for more info """ + if self.method == VKCaptchaEnm.VKCaptchaImageTask: + await self._aio_body_file_processing( + save_format=self.save_format, + file_path=self.img_path, + image_key="image", + captcha_link=captcha_link, + captcha_file=captcha_file, + captcha_base64=captcha_base64, + ) + if steps: + self.create_task_payload["task"]["steps"] = steps + if not self.result.errorId: + return await self._aio_processing_response() + return self.result.to_dict() + return await self._aio_processing_response() diff --git a/tests/test_binance.py b/tests/test_binance.py new file mode 100644 index 00000000..20299276 --- /dev/null +++ b/tests/test_binance.py @@ -0,0 +1,197 @@ +import pytest + +from tests.conftest import BaseTest +from python_rucaptcha.core.enums import BinanceCaptchaEnm +from python_rucaptcha.binance_captcha import BinanceCaptcha +from python_rucaptcha.core.serializer import GetTaskResultResponseSer + + +class TestBinanceCaptcha(BaseTest): + websiteURL = "https://example.com/page-with-binance" + websiteKey = "login" + validateId = "cb0bfefa598c4d2a8b65f31fde54ecd57b" + userAgent = ( + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " + "(KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36" + ) + kwargs_params = { + "proxyType": "socks5", + "proxyAddress": BaseTest.proxyAddress, + "proxyPort": BaseTest.proxyPort, + } + + def test_methods_exists(self): + assert "captcha_handler" in BinanceCaptcha.__dict__.keys() + assert "aio_captcha_handler" in BinanceCaptcha.__dict__.keys() + + @pytest.mark.parametrize("method", BinanceCaptchaEnm.list_values()) + def test_args(self, method: str): + kwargs = {} + if method == BinanceCaptchaEnm.BinanceTask.value: + kwargs = {"proxyType": "http", "proxyAddress": "1.2.3.4", "proxyPort": 8080} + instance = BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + userAgent=self.userAgent, + method=method, + **kwargs, + ) + assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY + assert instance.create_task_payload["task"]["type"] == method + assert instance.create_task_payload["task"]["websiteURL"] == self.websiteURL + assert instance.create_task_payload["task"]["websiteKey"] == self.websiteKey + assert instance.create_task_payload["task"]["validateId"] == self.validateId + assert instance.create_task_payload["task"]["userAgent"] == self.userAgent + + def test_kwargs(self): + instance = BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTask, + **self.kwargs_params, + ) + assert set(self.kwargs_params.keys()).issubset(set(instance.create_task_payload["task"].keys())) + assert set(self.kwargs_params.values()).issubset(set(instance.create_task_payload["task"].values())) + + def test_proxy_params_in_payload(self): + instance = BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTask, + proxyType="http", + proxyAddress="1.2.3.4", + proxyPort=8080, + ) + assert instance.create_task_payload["task"]["proxyType"] == "http" + assert instance.create_task_payload["task"]["proxyAddress"] == "1.2.3.4" + assert instance.create_task_payload["task"]["proxyPort"] == 8080 + + def test_no_useragent(self): + instance = BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTaskProxyless, + ) + assert "userAgent" not in instance.create_task_payload["task"] + + def test_missing_proxy_for_proxy_method(self): + with pytest.raises(ValueError, match="proxyType|proxyAddress|proxyPort"): + BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTask, + ) + + def test_wrong_method(self): + with pytest.raises(ValueError): + BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=self.get_random_string(5), + ) + + """ + Success tests + """ + + def test_basic_data(self): + instance = BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTaskProxyless.value, + ) + + result = instance.captcha_handler() + + assert isinstance(result, dict) is True + if not result["errorId"]: + assert result["status"] in ("ready", "processing") + assert isinstance(result["taskId"], int) is True + else: + assert result["errorId"] in (1, 12) + assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" + + assert result.keys() == GetTaskResultResponseSer().to_dict().keys() + + async def test_aio_basic_data(self): + instance = BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTaskProxyless.value, + ) + + result = await instance.aio_captcha_handler() + + assert isinstance(result, dict) is True + if not result["errorId"]: + assert result["status"] in ("ready", "processing") + assert isinstance(result["taskId"], int) is True + else: + assert result["errorId"] in (1, 12) + assert result["errorCode"] in ("ERROR_CAPTCHA_UNSOLVABLE", BinanceCaptcha.NO_CAPTCHA_ERR) + + assert result.keys() == GetTaskResultResponseSer().to_dict().keys() + + def test_context_basic_data(self): + with BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTaskProxyless.value, + ) as instance: + assert instance + + async def test_context_aio_basic_data(self): + async with BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + validateId=self.validateId, + method=BinanceCaptchaEnm.BinanceTaskProxyless.value, + ) as instance: + assert instance + + """ + Fail tests + """ + + def test_no_websiteURL(self): + with pytest.raises(TypeError): + BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteKey=self.websiteKey, + validateId=self.validateId, + ) + + def test_no_websiteKey(self): + with pytest.raises(TypeError): + BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + validateId=self.validateId, + ) + + def test_no_validateId(self): + with pytest.raises(TypeError): + BinanceCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + websiteURL=self.websiteURL, + websiteKey=self.websiteKey, + ) diff --git a/tests/test_vk_captcha.py b/tests/test_vk_captcha.py new file mode 100644 index 00000000..e3022e18 --- /dev/null +++ b/tests/test_vk_captcha.py @@ -0,0 +1,175 @@ +import pytest + +from tests.conftest import BaseTest +from python_rucaptcha.core.enums import VKCaptchaEnm +from python_rucaptcha.vk_captcha import VKCaptcha +from python_rucaptcha.core.serializer import GetTaskResultResponseSer + + +class TestVKCaptcha(BaseTest): + redirectUri = "https://id.vk.com/not_robot_captcha?domain=vk.com" + userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + captcha_link = "https://vk.com/captcha.php?sid=123456" + + def test_methods_exists(self): + assert "captcha_handler" in VKCaptcha.__dict__.keys() + assert "aio_captcha_handler" in VKCaptcha.__dict__.keys() + + @pytest.mark.parametrize("method", VKCaptchaEnm.list_values()) + def test_args(self, method: str): + instance = VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + method=method, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + proxyType="socks5", + proxyAddress=self.proxyAddress, + proxyPort=self.proxyPort, + ) + assert instance.create_task_payload["clientKey"] == self.RUCAPTCHA_KEY + assert instance.create_task_payload["task"]["type"] == method + + def test_vkcaptcha_task_payload(self): + instance = VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + proxyType="socks5", + proxyAddress=self.proxyAddress, + proxyPort=self.proxyPort, + ) + task = instance.create_task_payload["task"] + assert task["type"] == "VKCaptchaTask" + assert task["redirectUri"] == self.redirectUri + assert task["userAgent"] == self.userAgent + assert task["proxyType"] == "socks5" + assert task["proxyAddress"] == self.proxyAddress + assert task["proxyPort"] == self.proxyPort + + def test_vkcaptcha_task_with_proxy_auth(self): + instance = VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + proxyType="socks5", + proxyAddress=self.proxyAddress, + proxyPort=self.proxyPort, + proxyLogin="user1", + proxyPassword="pass1", + ) + task = instance.create_task_payload["task"] + assert task["proxyLogin"] == "user1" + assert task["proxyPassword"] == "pass1" + + def test_vkcaptcha_image_task_no_proxy_required(self): + instance = VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + method=VKCaptchaEnm.VKCaptchaImageTask, + ) + assert instance.create_task_payload["task"]["type"] == "VKCaptchaImageTask" + assert "redirectUri" not in instance.create_task_payload["task"] + + def test_vkcaptcha_image_task_default_method(self): + instance = VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + ) + assert instance.create_task_payload["task"]["type"] == "VKCaptchaTask" + + """ + Fail tests + """ + + def test_wrong_method(self): + with pytest.raises(ValueError): + VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + method=self.get_random_string(length=5), + ) + + def test_vkcaptcha_task_missing_required_params(self): + with pytest.raises(ValueError): + VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + method=VKCaptchaEnm.VKCaptchaTask, + ) + + def test_vkcaptcha_task_missing_proxy(self): + with pytest.raises(ValueError): + VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + method=VKCaptchaEnm.VKCaptchaTask, + ) + + """ + Success tests + """ + + def test_basic_data(self): + instance = VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + proxyType="socks5", + proxyAddress=self.proxyAddress, + proxyPort=self.proxyPort, + ) + + result = instance.captcha_handler() + + assert isinstance(result, dict) is True + if not result["errorId"]: + assert result["status"] == "ready" + assert isinstance(result["solution"], dict) is True + assert isinstance(result["taskId"], int) is True + else: + assert result["errorId"] in (1, 12) + assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" + + assert result.keys() == GetTaskResultResponseSer().to_dict().keys() + + async def test_aio_basic_data(self): + instance = VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + proxyType="socks5", + proxyAddress=self.proxyAddress, + proxyPort=self.proxyPort, + ) + + result = await instance.aio_captcha_handler() + + assert isinstance(result, dict) is True + if not result["errorId"]: + assert result["status"] == "ready" + assert isinstance(result["solution"], dict) is True + assert isinstance(result["taskId"], int) is True + else: + assert result["errorId"] in (1, 12) + assert result["errorCode"] == "ERROR_CAPTCHA_UNSOLVABLE" + + assert result.keys() == GetTaskResultResponseSer().to_dict().keys() + + def test_context_basic_data(self): + with VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + proxyType="socks5", + proxyAddress=self.proxyAddress, + proxyPort=self.proxyPort, + ) as instance: + assert instance + + async def test_context_aio_basic_data(self): + async with VKCaptcha( + rucaptcha_key=self.RUCAPTCHA_KEY, + redirectUri=self.redirectUri, + userAgent=self.userAgent, + proxyType="socks5", + proxyAddress=self.proxyAddress, + proxyPort=self.proxyPort, + ) as instance: + assert instance