From 24b1dc6f0694426cb3caf085179f51e2d8611e64 Mon Sep 17 00:00:00 2001 From: alperensert <63921520+alperensert@users.noreply.github.com> Date: Wed, 30 Apr 2025 13:14:51 +0300 Subject: [PATCH 1/5] feat(core): rewrite Capmonster client with Pydantic v2 and modern Python features - Replaced legacy procedural structure with fully typed class-based design - Introduced Pydantic v2 models with field validation, serialization, and aliasing - Implemented sync and async Capmonster clients using httpx - Added extensible BaseTaskPayload structure with abstract to_request() - Included validator and field_serializer usage for dynamic task behavior - Flattened proxy structure via smart task serialization - Applied frozen and immutable model config where applicable - Full support for Literal, Optional etc. - Some test cases prepared with pytest + pytest-asyncio - Ready for mkdocs documentation generation BREAKING CHANGE: API usage has completely changed from v3.2 to v4.0. All task creation methods now rely on structured payloads and typed clients. --- capmonster_python/__init__.py | 29 +- capmonster_python/aws_waf.py | 22 - capmonster_python/capmonster.py | 253 - capmonster_python/client.py | 249 + capmonster_python/compleximage.py | 51 - capmonster_python/datadome.py | 19 - capmonster_python/exceptions.py | 9 + capmonster_python/fun_captcha.py | 30 - capmonster_python/geetest.py | 26 - capmonster_python/hcaptcha.py | 36 - capmonster_python/image_to_text.py | 43 - capmonster_python/methods.py | 32 + capmonster_python/recaptcha_v2.py | 27 - capmonster_python/recaptcha_v2_enterprise.py | 30 - capmonster_python/recaptcha_v3.py | 24 - capmonster_python/tasks/basilisk.py | 23 + capmonster_python/tasks/binance.py | 35 + .../tasks/complex_image_recaptcha.py | 68 + .../tasks/complex_image_recognition.py | 33 + capmonster_python/tasks/datadome.py | 56 + capmonster_python/tasks/geetest.py | 97 + capmonster_python/tasks/image_to_text.py | 37 + capmonster_python/tasks/imperva.py | 54 + capmonster_python/tasks/prosopo.py | 32 + capmonster_python/tasks/recaptcha_v2.py | 45 + .../tasks/recaptcha_v2_enterprise.py | 44 + capmonster_python/tasks/recaptcha_v3.py | 36 + capmonster_python/tasks/task.py | 68 + capmonster_python/tasks/temu.py | 37 + capmonster_python/tasks/tendi.py | 30 + capmonster_python/tasks/turnstile.py | 81 + capmonster_python/tendi.py | 19 - capmonster_python/turnstile.py | 23 - capmonster_python/utils.py | 35 - docs/gatsby-config.js | 53 - docs/package.json | 37 - .../gatsby-theme-docs/text/index.mdx | 19 - docs/src/config/sidebar.yml | 56 - docs/src/docs/api/class-capmonster.mdx | 32 - docs/src/docs/api/class-funcap.mdx | 24 - docs/src/docs/api/class-geetest.mdx | 23 - docs/src/docs/api/class-hcap.mdx | 25 - docs/src/docs/api/class-img-to-text.mdx | 24 - docs/src/docs/api/class-proxy.mdx | 18 - docs/src/docs/api/class-recap-v2.mdx | 23 - docs/src/docs/api/class-recap-v3.mdx | 22 - docs/src/docs/api/class-turnstile.mdx | 20 - docs/src/docs/api/class-useragent.mdx | 13 - docs/src/docs/error-types.mdx | 34 - docs/src/docs/examples.mdx | 35 - docs/src/docs/installation.mdx | 10 - docs/src/docs/no-cache.mdx | 16 - docs/src/docs/usage/proxy-and-ua.mdx | 79 - docs/src/docs/usage/solve-funcap.mdx | 27 - docs/src/docs/usage/solve-geetest.mdx | 34 - docs/src/docs/usage/solve-hcaptcha.mdx | 21 - docs/src/docs/usage/solve-img-to-text.mdx | 37 - docs/src/docs/usage/solve-recap-v2.mdx | 29 - docs/src/docs/usage/solve-recap-v3.mdx | 27 - docs/src/docs/usage/solve-turnstile.mdx | 23 - docs/src/pages/404.js | 18 - docs/src/yamlFiles/letters.yml | 3 - docs/static/banner.png | Bin 26856 -> 0 bytes docs/static/favicon.png | Bin 8802 -> 0 bytes docs/static/geetest.jpg | Bin 10077 -> 0 bytes docs/static/image-captchas.png | Bin 146605 -> 0 bytes docs/yarn.lock | 11974 ---------------- pytest.ini | 3 + requirements.txt | 2 + tests/basilisk_test.py | 36 + tests/binance_test.py | 40 + tests/client_test.py | 22 + tests/complex_image_recaptcha_test.py | 32 + tests/recaptcha_v2_test.py | 36 + 74 files changed, 1253 insertions(+), 13407 deletions(-) delete mode 100644 capmonster_python/aws_waf.py delete mode 100644 capmonster_python/capmonster.py create mode 100644 capmonster_python/client.py delete mode 100644 capmonster_python/compleximage.py delete mode 100644 capmonster_python/datadome.py create mode 100644 capmonster_python/exceptions.py delete mode 100644 capmonster_python/fun_captcha.py delete mode 100644 capmonster_python/geetest.py delete mode 100644 capmonster_python/hcaptcha.py delete mode 100644 capmonster_python/image_to_text.py create mode 100644 capmonster_python/methods.py delete mode 100644 capmonster_python/recaptcha_v2.py delete mode 100644 capmonster_python/recaptcha_v2_enterprise.py delete mode 100644 capmonster_python/recaptcha_v3.py create mode 100644 capmonster_python/tasks/basilisk.py create mode 100644 capmonster_python/tasks/binance.py create mode 100644 capmonster_python/tasks/complex_image_recaptcha.py create mode 100644 capmonster_python/tasks/complex_image_recognition.py create mode 100644 capmonster_python/tasks/datadome.py create mode 100644 capmonster_python/tasks/geetest.py create mode 100644 capmonster_python/tasks/image_to_text.py create mode 100644 capmonster_python/tasks/imperva.py create mode 100644 capmonster_python/tasks/prosopo.py create mode 100644 capmonster_python/tasks/recaptcha_v2.py create mode 100644 capmonster_python/tasks/recaptcha_v2_enterprise.py create mode 100644 capmonster_python/tasks/recaptcha_v3.py create mode 100644 capmonster_python/tasks/task.py create mode 100644 capmonster_python/tasks/temu.py create mode 100644 capmonster_python/tasks/tendi.py create mode 100644 capmonster_python/tasks/turnstile.py delete mode 100644 capmonster_python/tendi.py delete mode 100644 capmonster_python/turnstile.py delete mode 100644 capmonster_python/utils.py delete mode 100644 docs/gatsby-config.js delete mode 100644 docs/package.json delete mode 100644 docs/src/@rocketseat/gatsby-theme-docs/text/index.mdx delete mode 100644 docs/src/config/sidebar.yml delete mode 100644 docs/src/docs/api/class-capmonster.mdx delete mode 100644 docs/src/docs/api/class-funcap.mdx delete mode 100644 docs/src/docs/api/class-geetest.mdx delete mode 100644 docs/src/docs/api/class-hcap.mdx delete mode 100644 docs/src/docs/api/class-img-to-text.mdx delete mode 100644 docs/src/docs/api/class-proxy.mdx delete mode 100644 docs/src/docs/api/class-recap-v2.mdx delete mode 100644 docs/src/docs/api/class-recap-v3.mdx delete mode 100644 docs/src/docs/api/class-turnstile.mdx delete mode 100644 docs/src/docs/api/class-useragent.mdx delete mode 100644 docs/src/docs/error-types.mdx delete mode 100644 docs/src/docs/examples.mdx delete mode 100644 docs/src/docs/installation.mdx delete mode 100644 docs/src/docs/no-cache.mdx delete mode 100644 docs/src/docs/usage/proxy-and-ua.mdx delete mode 100644 docs/src/docs/usage/solve-funcap.mdx delete mode 100644 docs/src/docs/usage/solve-geetest.mdx delete mode 100644 docs/src/docs/usage/solve-hcaptcha.mdx delete mode 100644 docs/src/docs/usage/solve-img-to-text.mdx delete mode 100644 docs/src/docs/usage/solve-recap-v2.mdx delete mode 100644 docs/src/docs/usage/solve-recap-v3.mdx delete mode 100644 docs/src/docs/usage/solve-turnstile.mdx delete mode 100644 docs/src/pages/404.js delete mode 100644 docs/src/yamlFiles/letters.yml delete mode 100644 docs/static/banner.png delete mode 100644 docs/static/favicon.png delete mode 100644 docs/static/geetest.jpg delete mode 100644 docs/static/image-captchas.png delete mode 100644 docs/yarn.lock create mode 100644 pytest.ini create mode 100644 requirements.txt create mode 100644 tests/basilisk_test.py create mode 100644 tests/binance_test.py create mode 100644 tests/client_test.py create mode 100644 tests/complex_image_recaptcha_test.py create mode 100644 tests/recaptcha_v2_test.py diff --git a/capmonster_python/__init__.py b/capmonster_python/__init__.py index 74caf5b..1d87710 100644 --- a/capmonster_python/__init__.py +++ b/capmonster_python/__init__.py @@ -1,13 +1,16 @@ -from .recaptcha_v2 import RecaptchaV2Task -from .recaptcha_v2_enterprise import RecaptchaV2EnterpriseTask -from .fun_captcha import FuncaptchaTask -from .recaptcha_v3 import RecaptchaV3Task -from .hcaptcha import HCaptchaTask -from .image_to_text import ImageToTextTask -from .geetest import GeeTestTask -from .utils import CapmonsterException -from .compleximage import ComplexImageTask -from .datadome import DataDomeTask -from .turnstile import TurnstileTask -from .tendi import TenDITask -from .aws_waf import AmazonWafTask +from .client import CapmonsterClient +from .tasks.basilisk import BasiliskTask +from .tasks.binance import BinanceTask +from .tasks.complex_image_recaptcha import ComplexImageRecaptchaTask, ComplexImageRecaptchaMetadata +from .tasks.complex_image_recognition import ComplexImageRecognitionTask +from .tasks.datadome import DataDomeTask, DataDomeMetadata +from .tasks.geetest import GeeTestV4Task, GeeTestV3Task +from .tasks.image_to_text import ImageToTextTask +from .tasks.imperva import ImpervaTask, ImpervaTaskMetadata +from .tasks.prosopo import ProsopoTask +from .tasks.recaptcha_v2 import RecaptchaV2Task +from .tasks.recaptcha_v2_enterprise import RecaptchaV2EnterpriseTask +from .tasks.recaptcha_v3 import RecaptchaV3Task +from .tasks.temu import TemuTask +from .tasks.tendi import TenDITask +from .tasks.turnstile import TurnstileTask, TurnstileCloudFlareTask diff --git a/capmonster_python/aws_waf.py b/capmonster_python/aws_waf.py deleted file mode 100644 index 144a9bf..0000000 --- a/capmonster_python/aws_waf.py +++ /dev/null @@ -1,22 +0,0 @@ -from .capmonster import Capmonster - -class AmazonWafTask(Capmonster): - def __init__(self, client_key): - super(AmazonWafTask, self).__init__(client_key) - - def create_task(self, website_url: str, website_key: str, challenge_script: str, captcha_script: str, - context: str, iv: str, cookieSolution: bool = False): - data = { - "clientKey": self._client_key, - "task": { - "type": "AmazonTaskProxyless", - "websiteURL": website_url, - "websiteKey": website_key, - "challengeScript": challenge_script, - "captchaScript": captcha_script, - "context": context, - "iv": iv, - "cookieSolution": cookieSolution - } - } - return self._make_request("createTask", data).get("taskId") \ No newline at end of file diff --git a/capmonster_python/capmonster.py b/capmonster_python/capmonster.py deleted file mode 100644 index 6436fb5..0000000 --- a/capmonster_python/capmonster.py +++ /dev/null @@ -1,253 +0,0 @@ -import requests -import asyncio -from time import sleep -from .utils import * - - -class Capmonster: - """A class that interacts with the Capmonster API.""" - __SOFT_ID = 30 - _HOST_URL = "https://api.capmonster.cloud" - _CREATE_TASK_URL = "/createTask" - _TASK_RESULT_URL = "/getTaskResult" - _BALANCE_URL = "/getBalance" - _INCORRECT_IMAGE_CAPTCHA_URL = "/reportIncorrectImageCaptcha" - _INCORRECT_TOKEN_CAPTCHA_URL = "/reportIncorrectTokenCaptcha" - - def __init__(self, client_key): - self._client_key = client_key - - def get_balance(self) -> float: - """ - Retrieves the balance of the client. - - :return: The balance of the client as a float. - """ - data = {"clientKey": self._client_key} - return self._make_request("getBalance", data).get("balance") - - def get_task_result(self, task_id: int): - """ - :param task_id: The ID of the task for which the result is requested. - :return: The result of the task if it is ready, otherwise False. - """ - data = { - "clientKey": self._client_key, - "taskId": task_id - } - result = self._make_request("getTaskResult", data) - is_ready = self._is_ready(result) - if is_ready: - return result.get("solution") - else: - return False - - def join_task_result(self, task_id: int, maximum_time: int = 120): - for i in range(0, maximum_time + 1, 2): - result = self.get_task_result(task_id) - if result is not False and result is not None: - return result - elif result is False: - i += 1 - sleep(2) - raise CapmonsterException( - 61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed.") - - async def join_task_result_async(self, task_id: int, maximum_time: int = 120): - for i in range(0, maximum_time + 1, 2): - result = self.get_task_result(task_id) - if result is not False and result is not None: - return result - elif result is False: - i += 1 - await asyncio.sleep(2) - raise CapmonsterException( - 61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed.") - - def report_incorrect_captcha(self, captcha_type: str, task_id: int) -> bool: - """ - Reports an incorrect captcha. - - :param captcha_type: The type of captcha ("image" or "token"). - :param task_id: The ID of the task. - :return: True if the captcha is successfully reported, False otherwise. - :raises CapmonsterException: If the captcha type is invalid. - """ - valid_captcha_types = ["image", "token"] - - if captcha_type not in valid_captcha_types: - raise CapmonsterException( - 1, "ERROR_INCORRECT_CAPTCHA_TYPE", "Valid captcha_type parameters are only 'image' or 'token'.") - try: - self._report_incorrect_captcha( - captcha_type=captcha_type, task_id=task_id) - return True - except: - return False - - @check_response() - def _report_incorrect_captcha(self, captcha_type: str, task_id: int): - data = { - "clientKey": self._client_key, - "taskId": task_id - } - if captcha_type == "image": - response = self._make_request("reportIncorrectImageCaptcha", data) - else: - response = self._make_request("reportIncorrectTokenCaptcha", data) - return response - - @staticmethod - def _is_ready(response: dict): - status = response.get("status") - if status == "processing": - return False - elif status == "ready": - return True - - @check_response() - def _make_request(self, method: str, data: dict): - _method = None - if method == "getBalance": - _method = self._BALANCE_URL - elif method == "getTaskResult": - _method = self._TASK_RESULT_URL - elif method == "createTask": - _method = self._CREATE_TASK_URL - data["softId"] = self.__SOFT_ID - elif method == "reportIncorrectImageCaptcha": - _method = self._INCORRECT_IMAGE_CAPTCHA_URL - elif method == "reportIncorrectTokenCaptcha": - _method = self._INCORRECT_TOKEN_CAPTCHA_URL - try: - response = requests.post("{}{}".format( - self._HOST_URL, _method), json=data).json() - except Exception as err: - raise CapmonsterException(-1, type(err).__name__, str(err)) - return response - - @staticmethod - def _add_cookies(cookies, data): - if cookies is None: - return data - str_cookies = "" - if type(cookies) is dict: - for key, value in cookies.items(): - if value == list(cookies.items())[-1][1]: - str_cookies += "{}={}".format(key, value) - else: - str_cookies += "{}={};".format(key, value) - elif type(cookies) is list: - for i in cookies: - if not len(cookies) % 2 == 0: - raise AttributeError( - "List cookies length must be even numbers") - if cookies.index(i) % 2 == 0: - str_cookies += "{}=".format(i) - elif cookies[cookies.index(i)] == cookies[-1]: - str_cookies += "{}".format(i) - elif cookies.index(i) % 2 == 1: - str_cookies += "{};".format(i) - elif type(cookies) is str: - data["task"]["cookies"] = cookies - return data - data["task"]["cookies"] = str_cookies - return data - - -class Proxy(Capmonster): - def __init__(self, client_key): - super().__init__(client_key) - self._proxy_type = None - self._proxy_address = None - self._proxy_port = None - self._proxy_login = None - self._proxy_password = None - - def set_proxy(self, proxy_type: str, proxy_address: str, proxy_port: int, - proxy_login: str = None, proxy_password: str = None): - """ - Sets the proxy settings for the client. - - :param proxy_type: The type of the proxy. (e.g. "HTTP", "SOCKS5") - :param proxy_address: The address of the proxy server. - :param proxy_port: The port number of the proxy server. - :param proxy_login: (optional) The login username for proxy authentication. - :param proxy_password: (optional) The login password for proxy authentication. - :return: None - """ - self._proxy_type = proxy_type - self._proxy_address = proxy_address - self._proxy_port = proxy_port - self._proxy_login = proxy_login - self._proxy_password = proxy_password - - def disable_proxy(self): - """ - Disables the proxy settings. - - :return: None - """ - self._proxy_type = None - self._proxy_address = None - self._proxy_port = None - self._proxy_login = None - self._proxy_password = None - - def _is_proxy_task(self, data: dict): - """ - Determine for is this a proxy task or not? - e.g. if you are not set the values with set_proxy method, it is a proxyless method, or if you are set up it is a - proxy task. - """ - if self._proxy_type and self._proxy_address and self._proxy_port: - data["task"]["proxyType"] = self._proxy_type - data["task"]["proxyAddress"] = self._proxy_address - data["task"]["proxyPort"] = self._proxy_port - if self._proxy_login and self._proxy_password: - data["task"]["proxyLogin"] = self._proxy_login - data["task"]["proxyPassword"] = self._proxy_password - return data, True - data["task"]["type"] += "Proxyless" - return data, False - - -class UserAgent(Capmonster): - def __init__(self, client_key): - super().__init__(client_key) - self._user_agent = None - self._fallback = False - - def set_user_agent(self, user_agent: str): - """ - Set the user agent for the instance. - - :param user_agent: The user agent string to set. - :return: None - """ - self._user_agent = user_agent - - def reset_user_agent(self): - """ - Reset the user agent to None. - - :return: - """ - self._user_agent = None - - def _add_user_agent(self, data): - data["task"]["fallbackToActualUA"] = self._fallback - if self._user_agent: - data["task"]["userAgent"] = self._user_agent - return data, True - return data, False - - def set_fallback_to_actual_user_agent(self, fallback: bool): - """ - Set the fallback value for the actual user agent. - - :param fallback: A boolean value indicating whether to enable or disable the fallback. - :type fallback: bool - :return: None - """ - self._fallback = fallback diff --git a/capmonster_python/client.py b/capmonster_python/client.py new file mode 100644 index 0000000..aa1f25e --- /dev/null +++ b/capmonster_python/client.py @@ -0,0 +1,249 @@ +import asyncio +from time import sleep +from typing import Optional + +from httpx import Response, AsyncClient, Client + +from capmonster_python.exceptions import CapmonsterException +from capmonster_python.methods import GetBalancePayload, GetTaskResultPayload, CreateTaskPayload +from capmonster_python.tasks.task import TaskPayload + + +class CapmonsterClient: + __BASE_URL = "https://api.capmonster.cloud" + __BALANCE_URL = "/getBalance" + __TASK_RESULT_URL = "/getTaskResult" + __CREATE_TASK_URL = "/createTask" + __MAX_RETRY = 120 + __PER_RETRY_DELAY = 1 + + def __init__(self, api_key: str, timeout: Optional[float] = 30.0) -> None: + self.api_key = api_key + self.__async_client = AsyncClient(timeout=timeout, base_url=self.__BASE_URL) + self.__sync_client = Client(timeout=timeout, base_url=self.__BASE_URL) + + def get_balance(self) -> float: + """ + Fetches the current balance using the provided API key. + + Raises: + This method may raise exceptions if the HTTP request fails or the JSON response + format is unexpected. These exceptions are not handled within the method. + + Returns: + float: The balance fetched from the external service. Defaults to 0.0 if the balance + key is not found in the response. + """ + payload = GetBalancePayload(clientKey=self.api_key).model_dump() + response = self.__make_sync_request(self.__BALANCE_URL, payload).json() + return response.get("balance", 0.0) + + async def get_balance_async(self) -> float: + """ + Asynchronously fetches the current balance using the provided API key. + + Raises: + This method may raise exceptions if the HTTP request fails or the JSON response + format is unexpected. These exceptions are not handled within the method. + + Returns: + float: The balance fetched from the external service. Defaults to 0.0 if the balance + key is not found in the response. + """ + payload = GetBalancePayload(clientKey=self.api_key).model_dump() + response = await self.__make_async_request(self.__BALANCE_URL, payload) + return response.json().get("balance", 0.0) + + def create_task(self, task: TaskPayload, callback_url: Optional[str] = None) -> int: + """ + Creates a task based on the provided payload and returns the task ID. + + This method is responsible for initiating a task by preparing the request with the + provided task details and optional callback URL, sending a synchronous request to the + task creation endpoint, and parsing the response to retrieve the task ID. + + Args: + task: TaskPayload + The task configuration payload that defines the specifics of the task to be + created. + callback_url: Optional[str] + An optional URL to be called back upon task completion or update. + + Returns: + int + The unique identifier of the created task. + + Raises: + CapmonsterException: If the task creation request fails or the response is not contains valid task id. + """ + payload = CreateTaskPayload(clientKey=self.api_key, task=task, callbackUrl=callback_url).model_dump( + exclude_none=True) + payload["softId"] = 30 + response = self.__make_sync_request(self.__CREATE_TASK_URL, payload).json() + return response.get("taskId", 0) + + async def create_task_async(self, task: TaskPayload, callback_url: Optional[str] = None) -> int: + """ + Asynchronously creates a task based on the provided payload and returns the task ID. + + Args: + task: TaskPayload + The task configuration payload that defines the specifics of the task to be + created. + callback_url: Optional[str] + An optional URL to be called back upon task completion or update. + + Returns: + int + The unique identifier of the created task. + + Raises: + CapmonsterException: If the task creation request fails or the response is not contains valid task id. + """ + payload = CreateTaskPayload(clientKey=self.api_key, task=task, callbackUrl=callback_url).model_dump( + exclude_none=True) + payload["softId"] = 30 + request = await self.__make_async_request(self.__CREATE_TASK_URL, payload) + response = request.json() + return response.get("taskId", 0) + + def get_task_result(self, task_id: int) -> dict: + """ + Fetches the result of a specific task by its identifier. + + This function interacts with API to retrieve the results of a + previously submitted task. The function uses the provided task ID and + sends a request to the API endpoint to get the result. It returns the + solution data extracted from the API response or an empty dictionary if + no solution data is found. + + Args: + task_id (int): An integer representing the unique identifier of the task. + + Returns: + dict: A dictionary containing the solution data retrieved from the task + result API endpoint. If the solution is not found in the response, an empty + dictionary is returned. + """ + payload = GetTaskResultPayload(clientKey=self.api_key, taskId=task_id).model_dump() + response = self.__make_sync_request(self.__TASK_RESULT_URL, payload).json() + return response.get("solution", {}) + + async def get_task_result_async(self, task_id: int) -> dict: + """ + Asynchronously fetches the result of a specific task by its identifier. + + This function interacts with API to retrieve the results of a + previously submitted task. The function uses the provided task ID and + sends a request to the API endpoint to get the result. It returns the + solution data extracted from the API response or an empty dictionary if + no solution data is found. + + Args: + task_id (int): An integer representing the unique identifier of the task. + + Returns: + dict: A dictionary containing the solution data retrieved from the task + result API endpoint. If the solution is not found in the response, an empty + dictionary is returned. + """ + payload = GetTaskResultPayload(clientKey=self.api_key, taskId=task_id).model_dump() + request = await self.__make_async_request(self.__TASK_RESULT_URL, payload) + response = request.json() + return response.get("solution", {}) + + def join_task_result(self, task_id: int) -> dict: + """ + Joins and retrieves the result of a task given its ID, retrying a specified number + of times if the result is not immediately available. + + Parameters: + task_id: int + The identifier of the task for which the result needs to be retrieved. + + Returns: + dict: + The result of the task if successfully retrieved within the maximum retries. + + Raises: + CapmonsterException: + If the maximum retry limit is exceeded without obtaining the task result. + """ + for _ in range(0, self.__MAX_RETRY + 1): + result = self.get_task_result(task_id) + if result: + return result + elif not result: + sleep(self.__PER_RETRY_DELAY) + raise CapmonsterException( + 61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed.") + + async def join_task_result_async(self, task_id: int): + """ + Asynchronously joins and retrieves the result of a task given its ID, retrying a specified number + of times if the result is not immediately available. + + Parameters: + task_id: int + The identifier of the task for which the result needs to be retrieved. + + Returns: + dict: + The result of the task if successfully retrieved within the maximum retries. + + Raises: + CapmonsterException: + If the maximum retry limit is exceeded without obtaining the task result. + """ + for _ in range(0, self.__MAX_RETRY + 1): + result = await self.get_task_result_async(task_id) + if result: + return result + elif not result: + await asyncio.sleep(self.__PER_RETRY_DELAY) + raise CapmonsterException( + 61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed.") + + def __make_sync_request(self, url: str, payload: dict) -> Response: + try: + response = self.__sync_client.post(url, json=payload) + return self.__check_response(response) + except Exception as e: + raise CapmonsterException(-1, type(e).__name__, str(e)) + + async def __make_async_request(self, url: str, payload: dict) -> Response: + try: + post = await self.__async_client.post(url, json=payload) + return self.__check_response(post) + except Exception as e: + raise CapmonsterException(-1, type(e).__name__, str(e)) + + @staticmethod + def __is_ready(response: dict): + status = response.get("status") + if status == "processing": + return False + elif status == "ready": + return True + else: + raise CapmonsterException(error_id=-1, + error_code="CAPMONSTER_API_ERROR", + error_description="Sometimes can be happen if Capmonster " + "servers there is too much intensity") + + @staticmethod + def __check_response(data: Response) -> Response: + response = data.json() + if type(response) is dict: + if response.get("errorId", 0) != 0: + raise CapmonsterException( + response.get("errorId"), + response.get("errorCode", response.get("errorCode", "UNKNOWN_ERROR")), + response.get("errorDescription", response.get("errorDescription", "Unknown error")) + ) + return data + else: + raise CapmonsterException(error_id=-1, + error_code="CAPMONSTER_API_ERROR", + error_description="Sometimes can be happen if Capmonster " + "servers there is too much intensity") diff --git a/capmonster_python/compleximage.py b/capmonster_python/compleximage.py deleted file mode 100644 index d25b96e..0000000 --- a/capmonster_python/compleximage.py +++ /dev/null @@ -1,51 +0,0 @@ -from .capmonster import UserAgent - - -class ComplexImageTask(UserAgent): - __VALID_CLASSES = ["recaptcha", "hcaptcha"] - - def __init__(self, client_key): - super(ComplexImageTask, self).__init__(client_key) - - def create_task(self, _class: str, grid: str = None, - task_definition: str = None, - image_urls: list = None, - images_base64: list = None, - task: str = None, - websiteUrl: str = None): - if _class not in self.__VALID_CLASSES: - raise ValueError("Currently only recaptcha or hcaptcha is supported as _class value.") - data = { - "clientKey": self._client_key, - "task": { - "type": "ComplexImageTask", - "class": _class, - "metadata": {} - } - } - if image_urls is not None: - data["task"]["imageUrls"] = image_urls - elif images_base64 is not None: - data["task"]["imagesBase64"] = images_base64 - else: - raise ValueError("image_urls or images_base64 must be sent") - if _class == "recaptcha": - if grid is None: - raise ValueError("Grid parameter must sent with recaptcha") - else: - data["task"]["metadata"]["Grid"] = grid - if task is not None: - data["task"]["metadata"]["Task"] = task - elif task_definition is not None: - data["task"]["metadata"]["TaskDefinition"] = task_definition - else: - raise ValueError("task_definition or task parameter must be sent") - elif _class == "hcaptcha": - if task is not None: - data["task"]["metadata"]["Task"] = task - else: - raise ValueError("task parameter must be sent with hcaptcha") - if websiteUrl is not None: - data["task"]["websiteUrl"] = websiteUrl - data, is_user_agent = self._add_user_agent(data) - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/datadome.py b/capmonster_python/datadome.py deleted file mode 100644 index 6f24a12..0000000 --- a/capmonster_python/datadome.py +++ /dev/null @@ -1,19 +0,0 @@ -from .capmonster import UserAgent - - -class DataDomeTask(UserAgent): - def __init__(self, client_key): - super(DataDomeTask, self).__init__(client_key) - - def create_task(self, website_url: str, metadata: object): - data = { - "client_key": self._client_key, - "task": { - "type": "CustomTask", - "class": "DataDome", - "websiteURL": website_url, - "metadata": metadata, - } - } - data, is_user_agent = self._add_user_agent(data) - return self._make_request("createTask", data=data).get("taskId") diff --git a/capmonster_python/exceptions.py b/capmonster_python/exceptions.py new file mode 100644 index 0000000..65a532c --- /dev/null +++ b/capmonster_python/exceptions.py @@ -0,0 +1,9 @@ +class CapmonsterException(Exception): + def __init__(self, error_id, error_code, error_description, *args, **kwargs): + super(CapmonsterException, self).__init__("[{}:{}]{}".format(error_code, error_id, error_description)) + self.error_description = error_description + self.error_id = error_id + self.error_code = error_code + + def __str__(self): + return "[{}] {}".format(self.error_code, self.error_description) diff --git a/capmonster_python/fun_captcha.py b/capmonster_python/fun_captcha.py deleted file mode 100644 index 9434e23..0000000 --- a/capmonster_python/fun_captcha.py +++ /dev/null @@ -1,30 +0,0 @@ -from .capmonster import Proxy, UserAgent -from typing import Union - - -class FuncaptchaTask(UserAgent, Proxy): - def __init__(self, client_key): - super(FuncaptchaTask, self).__init__(client_key) - - def create_task(self, website_url: str, website_public_key: str, api_js_subdomain: str = None, - data_blob: str = None, cookies: Union[dict, list, str] = None, no_cache: bool = None): - data = { - "clientKey": self._client_key, - "task": { - "type": "FunCaptchaTask", - "websiteURL": website_url, - "websitePublicKey": website_public_key - } - } - if data_blob is not None: - data["task"]["data"] = data_blob - data, is_proxy = self._is_proxy_task(data) - if is_proxy: - data, is_user_agent = self._add_user_agent(data) - if cookies is not None: - data = self._add_cookies(cookies, data) - if api_js_subdomain is not None: - data["task"]["funcaptchaApiJSSubdomain"] = api_js_subdomain - if no_cache: - data["task"]["nocache"] = no_cache - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/geetest.py b/capmonster_python/geetest.py deleted file mode 100644 index 179fafe..0000000 --- a/capmonster_python/geetest.py +++ /dev/null @@ -1,26 +0,0 @@ -from .capmonster import Proxy, UserAgent - - -class GeeTestTask(UserAgent, Proxy): - def __init__(self, client_key): - super(GeeTestTask, self).__init__(client_key) - - def create_task(self, website_url: str, gt: str, challenge: str, - api_server_subdomain: str = None, get_lib: str = None): - data = { - "clientKey": self._client_key, - "task": { - "type": "GeeTestTask", - "websiteURL": website_url, - "gt": gt, - "challenge": challenge - } - } - data, is_user_agent = self._add_user_agent(data) - data, is_proxy = self._is_proxy_task(data) - if api_server_subdomain: - data["task"]["geetestApiServerSubdomain"] = api_server_subdomain - if get_lib: - data["task"]["geetestGetLib"] = get_lib - return self._make_request("createTask", data).get("taskId") - diff --git a/capmonster_python/hcaptcha.py b/capmonster_python/hcaptcha.py deleted file mode 100644 index 73302cb..0000000 --- a/capmonster_python/hcaptcha.py +++ /dev/null @@ -1,36 +0,0 @@ -from .capmonster import Proxy, UserAgent -from .utils import CapmonsterException -from typing import Union - - -class HCaptchaTask(UserAgent, Proxy): - def __init__(self, client_key): - super(HCaptchaTask, self).__init__(client_key) - - def create_task(self, website_url: str, website_key: str, is_invisible: bool = None, custom_data: str = None, - cookies: Union[dict, list, str] = None, no_cache: bool = None): - data = { - "clientKey": self._client_key, - "task": { - "type": "HCaptchaTask", - "websiteURL": website_url, - "websiteKey": website_key - } - } - if cookies is not None: - data = self._add_cookies(cookies, data) - if is_invisible is not None: - data["task"]["isInvisible"] = is_invisible - if custom_data is not None: - data, is_user_agent = self._add_user_agent(data) - if is_user_agent is False: - raise CapmonsterException(-1, - "USER_AGENT_ERROR", - "You must provide an user agent if you submit captcha with custom_data") - data["task"]["data"] = custom_data - if custom_data is None: - data, is_user_agent = self._add_user_agent(data) - data, is_proxy = self._is_proxy_task(data) - if no_cache: - data["task"]["nocache"] = no_cache - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/image_to_text.py b/capmonster_python/image_to_text.py deleted file mode 100644 index 9cfd58a..0000000 --- a/capmonster_python/image_to_text.py +++ /dev/null @@ -1,43 +0,0 @@ -from .capmonster import Capmonster, CapmonsterException -from base64 import b64encode - - -class ImageToTextTask(Capmonster): - def __init__(self, client_key): - super().__init__(client_key) - - def create_task(self, image_path: str = None, base64_encoded_image: str = None, module: str = None, - recognizing_threshold: int = None, case: bool = None, numeric: int = None, - math: bool = None): - if base64_encoded_image is None and image_path is None: - raise CapmonsterException(error_id=-1, - error_code="ERROR_NOTHING_GIVEN", - error_description="You have to give image_path or base64_encoded_image") - data = { - "clientKey": self._client_key, - "task": { - "type": "ImageToTextTask" - } - } - if base64_encoded_image is None: - img_body = self._from_path(image_path) - data["task"]["body"] = img_body - else: - data["task"]["body"] = base64_encoded_image - if module is not None: - data["task"]["CapMonsterModule"] = module - if recognizing_threshold is not None and (0 <= recognizing_threshold <= 100): - data["task"]["recognizingThreshold"] = recognizing_threshold - if case is not None: - data["task"]["Case"] = case - if numeric is not None and (0 <= numeric <= 1): - data["task"]["numeric"] = numeric - if math is not None: - data["task"]["math"] = math - return self._make_request("createTask", data).get("taskId") - - @staticmethod - def _from_path(image_path: str): - with open(image_path, "rb") as img: - base64_img = b64encode(img.read()).decode("ascii") - return base64_img diff --git a/capmonster_python/methods.py b/capmonster_python/methods.py new file mode 100644 index 0000000..a644086 --- /dev/null +++ b/capmonster_python/methods.py @@ -0,0 +1,32 @@ +from typing import Optional + +from pydantic import BaseModel, Field, field_serializer + +from .tasks.task import TaskPayload + + +class GetBalancePayload(BaseModel): + """ + Represents a payload for retrieving the balance. + + Attributes: + clientKey (str): The key representing the client, used for identifying + the client in balance-related operations. + """ + clientKey: str = Field(..., description="Client key") + + +class GetTaskResultPayload(BaseModel): + clientKey: str = Field(..., description="Client key") + taskId: int = Field(..., description="ID which was obtained from create task method.") + + +class CreateTaskPayload(BaseModel): + clientKey: str = Field(..., description="Client key") + task: TaskPayload = Field(..., description="Task data") + callbackUrl: Optional[str] = Field(default=None, + description="Web address for sending the captcha task result. Data is sent by POST request.") + + @field_serializer("task") + def serialize_task(self, task: TaskPayload, _info) -> dict: + return task.to_request() diff --git a/capmonster_python/recaptcha_v2.py b/capmonster_python/recaptcha_v2.py deleted file mode 100644 index 5eb18f9..0000000 --- a/capmonster_python/recaptcha_v2.py +++ /dev/null @@ -1,27 +0,0 @@ -from .capmonster import Proxy, UserAgent -from typing import Union - - -class RecaptchaV2Task(UserAgent, Proxy): - def __init__(self, client_key): - super(RecaptchaV2Task, self).__init__(client_key) - - def create_task(self, website_url: str, website_key: str, - cookies: Union[dict, list, str] = None, recaptcha_s_value: str = None, - no_cache: bool = None): - data = { - "clientKey": self._client_key, - "task": { - "type": "NoCaptchaTask", - "websiteURL": website_url, - "websiteKey": website_key - } - } - data, is_proxy = self._is_proxy_task(data) - data, is_user_agent = self._add_user_agent(data) - data = self._add_cookies(cookies, data) - if recaptcha_s_value is not None: - data["task"]["recaptchaDataSValue"] = recaptcha_s_value - if no_cache: - data["task"]["nocache"] = no_cache - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/recaptcha_v2_enterprise.py b/capmonster_python/recaptcha_v2_enterprise.py deleted file mode 100644 index 941a40e..0000000 --- a/capmonster_python/recaptcha_v2_enterprise.py +++ /dev/null @@ -1,30 +0,0 @@ -from typing import Union -from .capmonster import Proxy, UserAgent - - -class RecaptchaV2EnterpriseTask(UserAgent, Proxy): - def __init__(self, client_key): - super(RecaptchaV2EnterpriseTask, self).__init__(client_key) - - def create_task(self, website_url: str, website_key: str, - enterprise_payload=None, api_domain: str = None, - cookies: Union[dict, list, str] = None, - no_cache: bool = None): - data = { - "clientKey": self._client_key, - "task": { - "type": "RecaptchaV2EnterpriseTask", - "websiteURL": website_url, - "websiteKey": website_key - } - } - data, is_proxy = self._is_proxy_task(data) - data, is_user_agent = self._add_user_agent(data) - data = self._add_cookies(cookies, data) - if enterprise_payload is not None: - data["task"]["enterprisePayload"] = enterprise_payload - if api_domain is not None: - data["task"]["apiDomain"] = api_domain - if no_cache: - data["task"]["nocache"] = no_cache - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/recaptcha_v3.py b/capmonster_python/recaptcha_v3.py deleted file mode 100644 index cec0359..0000000 --- a/capmonster_python/recaptcha_v3.py +++ /dev/null @@ -1,24 +0,0 @@ -from .capmonster import Capmonster - - -class RecaptchaV3Task(Capmonster): - def __init__(self, client_key): - super().__init__(client_key) - - def create_task(self, website_url: str, website_key: str, minimum_score: float = 0.3, - page_action: str = None, no_cache: bool = None): - data = { - "clientKey": self._client_key, - "task": { - "type": "RecaptchaV3TaskProxyless", - "websiteURL": website_url, - "websiteKey": website_key - } - } - if 0.1 <= minimum_score <= 0.9: - data["task"]["minScore"] = minimum_score - if page_action is not None: - data["task"]["pageAction"] = page_action - if no_cache: - data["task"]["nocache"] = no_cache - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/tasks/basilisk.py b/capmonster_python/tasks/basilisk.py new file mode 100644 index 0000000..5dd0c96 --- /dev/null +++ b/capmonster_python/tasks/basilisk.py @@ -0,0 +1,23 @@ +from typing import Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload, UserAgentPayload + + +class BasiliskTask(TaskPayload, UserAgentPayload): + """ + BasiliskTask represents a specific task payload used for solving captcha on a given website. + + Attributes: + websiteURL (str): The address of the main page where the captcha is solved. + websiteKey (str): Captcha container's site key found in the HTML code's data-sitekey attribute. + """ + type: str = Field(default="CustomTask", frozen=True) + class_: str = Field(default="Basilisk", alias="class", frozen=True) + websiteURL: str = Field(..., description="The address of the main page where the captcha is solved.") + websiteKey: str = Field(..., + description="Can be found in the html code in the attribute data-sitekey of the captcha container.") + + def to_request(self) -> dict[str, Any]: + return self.model_dump(exclude_none=True, by_alias=True) diff --git a/capmonster_python/tasks/binance.py b/capmonster_python/tasks/binance.py new file mode 100644 index 0000000..ea92f7b --- /dev/null +++ b/capmonster_python/tasks/binance.py @@ -0,0 +1,35 @@ +from typing import Optional, Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload, UserAgentPayload, ProxyPayload + + +class BinanceTask(TaskPayload, UserAgentPayload): + """ + Represents a Binance task to solve captcha challenges with specific payload. Use only to log in with your account. + + This class encapsulates the payload required for a Binance captcha-solving task, including necessary user + agent and security details. It inherits common task and user agent payloads, allowing for integration + with systems handling such authentication processes. This class also supports optional proxy configuration. + + Attributes: + websiteURL: The address of the main page where the captcha is solved. + websiteKey: A unique parameter for your website's section. + validateId: A dynamic key representing the value of the parameter validateId, securityId, or + securityCheckResponseValidateId. + proxy: Optional proxy settings used for handling requests during captcha solving. + """ + type: str = Field(default="BinanceTask", frozen=True) + websiteURL: str = Field(..., description="The address of the main page where the captcha is solved.") + websiteKey: str = Field(..., description="A unique parameter for your website's section.") + validateId: str = Field(..., description="A dynamic key. The value of the parameter validateId, securityId, " + "or securityCheckResponseValidateId.") + proxy: Optional[ProxyPayload] = Field(default=None, description="Proxy settings.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True, by_alias=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/complex_image_recaptcha.py b/capmonster_python/tasks/complex_image_recaptcha.py new file mode 100644 index 0000000..864e63e --- /dev/null +++ b/capmonster_python/tasks/complex_image_recaptcha.py @@ -0,0 +1,68 @@ +from typing import List, Literal, Optional, Any + +from pydantic import Field, BaseModel, model_validator + +from capmonster_python.tasks.task import TaskPayload + + +class ComplexImageRecaptchaMetadata(BaseModel): + """ + Represents metadata for a complex image-based CAPTCHA task. + + Attributes: + task: Optional. Represents the textual description of the CAPTCHA task + (e.g., "Click on traffic lights"). Only one of `task` or `taskDefinition` + must be set. + taskDefinition: Optional. Represents the technical value defining the CAPTCHA + task type. Only one of `task` or `taskDefinition` must be set. + grid: Required. Specifies the grid size of the CAPTCHA. It must be one of + the possible values: "4x4", "3x3", or "1x1". + """ + task: Optional[str] = Field(default=None, alias="Task", + description="Possible values: \"Click on traffic lights\" and others.") + taskDefinition: Optional[str] = Field(default=None, alias="TaskDefinition", + description="Technical value that defines the task type.") + grid: Literal["4x4", "3x3", "1x1"] = Field(..., alias="Grid", + description="Grid size of the captcha part.") + + @model_validator(mode="after") + def check_fields_match_type(self) -> "ComplexImageRecaptchaMetadata": + if self.task is None and self.taskDefinition is None: + raise ValueError("task or taskDefinition must be set") + if self.task is not None and self.taskDefinition is not None: + raise ValueError("only one of task or taskDefinition must be set") + return self + + +class ComplexImageRecaptchaTask(TaskPayload): + """ + Represents a task payload for solving complex image-based recaptcha challenges. + + Attributes: + imageUrls: Optional list of image URLs associated with the captcha. These may + correspond to a single image captcha of formats 4x4, 3x3, or other new + configurations. + imagesBase64: Optional list of base64-encoded images. Similar to imageUrls, + these represent segments of the captcha in various formats such as 4x4, + 3x3, or others. + metadata: Metadata describing additional details required for solving the recaptcha + challenge. This attribute must be provided. + """ + type: str = Field(default="ComplexImageTask", frozen=True) + class_: str = Field(default="recaptcha", alias="class", frozen=True) + imageUrls: Optional[List[str]] = Field(default=None, + description="Single image 4x4, 3x3 or a new 1x1 captcha part (in an array).") + imagesBase64: Optional[List[str]] = Field(default=None, + description="Single image 4x4, 3x3 or a new 1x1 captcha part in base64 format (in an array).") + metadata: ComplexImageRecaptchaMetadata = Field(..., description="Metadata for the task.") + + @model_validator(mode="after") + def check_fields_match_type(self) -> "ComplexImageRecaptchaTask": + if self.imageUrls is None and self.imagesBase64 is None: + raise ValueError("imageUrls or imagesBase64 must be set") + if self.imageUrls is not None and self.imagesBase64 is not None: + raise ValueError("only one of imageUrls or imagesBase64 must be set") + return self + + def to_request(self) -> dict[str, Any]: + return self.model_dump(exclude_none=True, by_alias=True) diff --git a/capmonster_python/tasks/complex_image_recognition.py b/capmonster_python/tasks/complex_image_recognition.py new file mode 100644 index 0000000..d9d1950 --- /dev/null +++ b/capmonster_python/tasks/complex_image_recognition.py @@ -0,0 +1,33 @@ +from typing import List, Literal, Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload + + +class ComplexImageRecognitionTask(TaskPayload): + """ + Represents a complex image recognition task payload. + + Attributes: + imagesBase64: List of base64 encoded images. This is required and contains + raw image data in base64 format. + task: A string indicating one of the predefined types of recognition + task. This must be one of "oocl_rotate_new", "oocl_rotate_double_new", + "betpunch_3x3_rotate", "bls", "shein". + """ + type: str = Field(default="ComplexImageTask", frozen=True) + class_: str = Field(default="recognition", alias="class", frozen=True) + imagesBase64: List[str] = Field(..., description="List of base64 encoded images.") + task: Literal["oocl_rotate_new", "oocl_rotate_double_new", "betpunch_3x3_rotate", "bls", "shein"] = Field(..., + description="Type of task.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True, by_alias=True) + task = base.pop("task") + + if "metadata" not in base: + base["metadata"] = {} + + base["metadata"]["Task"] = task + return base diff --git a/capmonster_python/tasks/datadome.py b/capmonster_python/tasks/datadome.py new file mode 100644 index 0000000..01ca668 --- /dev/null +++ b/capmonster_python/tasks/datadome.py @@ -0,0 +1,56 @@ +from typing import Optional, Any + +from pydantic import Field, BaseModel, model_validator + +from capmonster_python.tasks.task import TaskPayload, UserAgentPayload, ProxyPayload + + +class DataDomeMetadata(BaseModel): + """ + Represents metadata related to DataDome. + + Attributes: + htmlPageBase64: Optional; Contains additional data about the captcha in + Base64-encoded format. + captchaUrl: Optional; URL pointing to the captcha, typically formatted + like: "https://geo.captcha-delivery.com/captcha/?initialCid=...". + datadomeCookie: Mandatory; Stores the DataDome cookies, retrievable on + the page using `document.cookie`. + """ + htmlPageBase64: Optional[str] = Field(default=None, + description="Object that contains additional data about the captcha.") + captchaUrl: Optional[str] = Field(default=None, + description="\"captchaUrl\" - link to the captcha. Usually it looks like this: \"https://geo.captcha-delivery.com/captcha/?initialCid=...\"") + datadomeCookie: str = Field(..., + description="Your cookies from datadome. You can get it on the page using document.cookie") + + @model_validator(mode="after") + def validate_fields(self) -> "DataDomeMetadata": + if self.htmlPageBase64 is None and self.captchaUrl is None: + raise ValueError("htmlPageBase64 or captchaUrl are required.") + if self.htmlPageBase64 is not None and self.captchaUrl is not None: + raise ValueError("htmlPageBase64 and captchaUrl are mutually exclusive.") + return self + + +class DataDomeTask(TaskPayload, UserAgentPayload): + """Represents a task payload for solving captchas on the DataDome platform. + + Attributes: + websiteURL: A string that contains the address of the page where the captcha is solved. + proxy: Optional proxy settings, represented as a ProxyPayload. Defaults to None. + metadata: Additional metadata about the captcha, represented as a DataDomeMetadata. + Defaults to None. + """ + type: str = Field(default="CustomTask", frozen=True) + class_: str = Field(default="DataDome", alias="class", frozen=True) + websiteURL: str = Field(..., description="Address of the page on which the captcha is solved.") + proxy: Optional[ProxyPayload] = Field(default=None, description="Proxy settings.") + metadata: DataDomeMetadata = Field(default=None, description="Additional data about the captcha.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True, by_alias=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/geetest.py b/capmonster_python/tasks/geetest.py new file mode 100644 index 0000000..dcbcdae --- /dev/null +++ b/capmonster_python/tasks/geetest.py @@ -0,0 +1,97 @@ +from typing import Optional, Any + +from pydantic import Field + +from .task import TaskPayload, ProxyPayload, UserAgentPayload + + +class GeeTestV3Task(TaskPayload, UserAgentPayload): + """ + Represents a payload for solving GeeTest V3 captcha. + + Attributes + ---------- + websiteURL : str + Address of the page on which the captcha is being solved. + gt : str + The GeeTest identifier key 'gt' corresponding to the domain. + challenge : str + A dynamic key that must be fresh on each request. Reusing a non-fresh challenge + value will result in an ERROR_TOKEN_EXPIRED. + geetestApiServerSubdomain : Optional[str] + Optional API subdomain server for Geetest that must be different from + `api.geetest.com`. + geetestGetLib : Optional[str] + Optional path to the captcha script. This must be provided as a JSON string. + proxy : Optional[ProxyPayload] + Optional proxy settings to be used while solving the captcha. + + Methods + ------- + to_request() + Constructs a dictionary representation of the payload, including proxy settings + if provided. + """ + type: str = Field(default="GeeTestTask", frozen=True) + websiteURL: str = Field(..., description="Address of the page on which the captcha is solved.") + gt: str = Field(..., description="The GeeTest identifier key gt for the domain.") + challenge: str = Field(..., description=( + "A dynamic key. Must be fresh on each request. If the captcha is loaded on the page, " + "then the challenge value is no longer valid and you will get ERROR_TOKEN_EXPIRED." + )) + geetestApiServerSubdomain: Optional[str] = Field( + default=None, description="Geetest API subdomain server (must differ from api.geetest.com)" + ) + geetestGetLib: Optional[str] = Field( + default=None, description="Path to captcha script. Must be passed as JSON string." + ) + proxy: Optional[ProxyPayload] = Field(default=None, description="Proxy settings.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base + + +class GeeTestV4Task(TaskPayload, UserAgentPayload): + """ + Handles the creation of a GeeTest v4 task payload for solving the GeeTest CAPTCHA. + + Attributes: + type: Specifies the type of the task. The value is fixed as "GeeTestTask". + websiteURL: Specifies the URL of the webpage where CAPTCHA is being solved. + gt: The GeeTest identifier key for the domain. + version: The version of the payload, which is fixed to 4. + initParameters: Optional. Additional parameters used for risk type identification or challenge details. + geetestApiServerSubdomain: Optional. Subdomain for the Geetest API server, differing from "api.geetest.com". + geetestGetLib: Optional. Path to the CAPTCHA script provided as a JSON string. + proxy: Optional. Proxy configuration used for solving CAPTCHA. + + Methods: + to_request: + Prepares a dictionary representation of the task payload, including proxy configuration if provided. + """ + type: str = Field(default="GeeTestTask", frozen=True) + websiteURL: str = Field(..., description="Address of the page on which the captcha is solved.") + gt: str = Field(..., description="The GeeTest identifier key gt for the domain.") + version: int = Field(default=4, frozen=True) + initParameters: Optional[object] = Field( + default=None, + description="Extra parameters for v4, used with 'riskType' or challenge details." + ) + geetestApiServerSubdomain: Optional[str] = Field( + default=None, description="Geetest API subdomain server (must differ from api.geetest.com)" + ) + geetestGetLib: Optional[str] = Field( + default=None, description="Path to captcha script. Must be passed as JSON string." + ) + proxy: Optional[ProxyPayload] = Field(default=None, description="Proxy settings.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/image_to_text.py b/capmonster_python/tasks/image_to_text.py new file mode 100644 index 0000000..3358e7e --- /dev/null +++ b/capmonster_python/tasks/image_to_text.py @@ -0,0 +1,37 @@ +from typing import Optional, Literal, Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload + + +class ImageToTextTask(TaskPayload): + """ + Represents a task for solving image-based captcha using certain recognition parameters. + + Attributes: + body (str): File body encoded in base64. Ensure it is passed without any line breaks. + capMonsterModule (Optional[str]): The name of the recognition module (e.g., "yandex"). + recognizingThreshold (Optional[int]): The threshold for captcha recognition confidence + with a range of 0 to 100. Ensures that you are charged only if recognition confidence + meets or exceeds this value. + case (Optional[bool]): Indicates whether the captcha is case-sensitive. + numeric (Optional[Literal[0, 1]]): Set to 1 if the captcha consists only of numbers. + math (Optional[bool]): Set to true if the captcha involves solving a mathematical operation. + """ + type: str = Field(default="ImageToTextTask", frozen=True) + body: str = Field(..., description="File body encoded in base64*. Make sure to pass it without line breaks.") + capMonsterModule: Optional[str] = Field(default=None, + description="The name of recognizing module, for example, “yandex“.") + recognizingThreshold: Optional[int] = Field(default=None, + description="Captcha recognition threshold with a possible value from " + "0 to 100. For example, if recognizingThreshold was set to " + "90 and the task was solved with a confidence of 80, " + "you won't be charged.", + ge=0, le=100) + case: Optional[bool] = Field(default=None, description="Set to true if captcha is case sensitive.") + numeric: Optional[Literal[0, 1]] = Field(default=None, description="Set to 1 if captcha contains numbers only.") + math: Optional[bool] = Field(default=None, description="Set to true if captcha requires a mathematical operation.") + + def to_request(self) -> dict[str, Any]: + return self.model_dump(exclude_none=True) diff --git a/capmonster_python/tasks/imperva.py b/capmonster_python/tasks/imperva.py new file mode 100644 index 0000000..a96a685 --- /dev/null +++ b/capmonster_python/tasks/imperva.py @@ -0,0 +1,54 @@ +from typing import Optional, Any + +from pydantic import Field, BaseModel + +from capmonster_python.tasks.task import TaskPayload, UserAgentPayload + + +class ImpervaTaskMetadata(BaseModel): + """ + Represents metadata related to an Imperva task. + + Attributes: + incapsulaScriptUrl: Name of the Incapsula JS file. + incapsulaCookie: Your cookies from Incapsula. They can be obtained on the + page using document.cookie. + reese84UrlEndpoint: Optional. The name of the endpoint where the reese84 + fingerprint is sent, found among the network requests and typically + ending with "?d=site.com". + """ + incapsulaScriptUrl: str = Field(..., description="Name of the Incapsula JS file.") + incapsulaCookie: str = Field(..., + description="Your cookies from Incapsula. They can be obtained on the page using document.cookie") + reese84UrlEndpoint: Optional[str] = Field(default=None, description="The name of the endpoint where the reese84 " + "fingerprint is sent can be found among the " + "requests and ends with ?d=site.com") + + +class ImpervaTask(TaskPayload, UserAgentPayload): + """ + Represents a task for handling and solving an Imperva-based challenge. + + Attributes: + type (str): The identifier for the type of task. This attribute is + frozen and defaults to "CustomTask". + + class_ (str): The identifier for the class of the task. This attribute is + frozen, defaults to "Imperva", and is aliased to "class" for external usage. + + websiteURL (str): The URL of the website targeted for solving the challenge. + + metadata (ImpervaTaskMetadata): Encapsulated metadata specific to the + Imperva challenge, such as the website key. + + Methods: + to_request: + Converts the task instance to a dictionary suitable for making a request. + """ + type: str = Field(default="CustomTask", frozen=True) + class_: str = Field(default="Imperva", alias="class", frozen=True) + websiteURL: str = Field(..., description="URL of the website to be solved.") + metadata: ImpervaTaskMetadata = Field(..., description="Website key.") + + def to_request(self) -> dict[str, Any]: + return self.model_dump(exclude_none=True, by_alias=True) diff --git a/capmonster_python/tasks/prosopo.py b/capmonster_python/tasks/prosopo.py new file mode 100644 index 0000000..b333746 --- /dev/null +++ b/capmonster_python/tasks/prosopo.py @@ -0,0 +1,32 @@ +from typing import Optional, Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload, ProxyPayload + + +class ProsopoTask(TaskPayload): + """ + Represents a Prosopo CAPTCHA solving task. + + Attributes: + websiteURL: The full URL of the CAPTCHA page that needs to be solved. + websiteKey: The site key parameter value obtained from the CAPTCHA page. + proxy: Optional proxy settings to be used in sending the task request. + + Methods: + to_request: + Converts the data of the task, including proxy settings if provided, + into a dictionary format suitable for creating task requests. + """ + type: str = Field(default="ProsopoTask", frozen=True) + websiteURL: str = Field(..., description="The full URL of the CAPTCHA page.") + websiteKey: str = Field(..., description="The value of the siteKey parameter found on the page.") + proxy: Optional[ProxyPayload] = Field(default=None, description="Proxy settings.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/recaptcha_v2.py b/capmonster_python/tasks/recaptcha_v2.py new file mode 100644 index 0000000..dd4ef86 --- /dev/null +++ b/capmonster_python/tasks/recaptcha_v2.py @@ -0,0 +1,45 @@ +from typing import Optional, Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload, ProxyPayload, UserAgentPayload + + +class RecaptchaV2Task(TaskPayload, UserAgentPayload): + """ + Represents a payload structure for solving reCAPTCHA v2 challenges. + + Attributes: + type: The constant string value identifying the task type as "RecaptchaV2Task". + websiteURL: The URL of the webpage containing the reCAPTCHA challenge. + websiteKey: The site key associated with the reCAPTCHA on the webpage. + recaptchaDataSValue: Optional. A one-time token specific to certain custom + implementations of reCAPTCHA v2. If applicable, this parameter needs + to be retrieved for each challenge solving attempt. + isInvisible: Optional. When set to True, specifies that the challenge + being solved is an invisible reCAPTCHA. + proxy: Optional. Proxy settings to route the request through a specified + proxy server. + + Methods: + to_request(): + Converts the instance into a dictionary suitable for request payloads, + integrating proxy settings if provided. + """ + type: str = Field(default="RecaptchaV2Task", frozen=True) + websiteURL: str = Field(..., description='Address of a webpage with captcha.') + websiteKey: str = Field(..., description='reCAPTCHA website key.') + recaptchaDataSValue: Optional[str] = Field(default=None, + description='Some custom implementations may contain additional "data-s" ' + 'parameter in ReCaptcha2 div, which is in fact a one-time token ' + 'and must be grabbed every time you want to solve a reCAPTCHA v2.') + isInvisible: Optional[bool] = Field(default=None, + description='Set to true if you want to solve invisible reCAPTCHA.') + proxy: Optional[ProxyPayload] = Field(default=None, description='Proxy settings.') + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/recaptcha_v2_enterprise.py b/capmonster_python/tasks/recaptcha_v2_enterprise.py new file mode 100644 index 0000000..f75f256 --- /dev/null +++ b/capmonster_python/tasks/recaptcha_v2_enterprise.py @@ -0,0 +1,44 @@ +from typing import Optional, Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload, ProxyPayload, UserAgentPayload + + +class RecaptchaV2EnterpriseTask(TaskPayload, UserAgentPayload): + """ + Represents a task payload for solving Google reCAPTCHA V2 Enterprise challenges. + + Attributes: + websiteURL: The address of a webpage with Google reCAPTCHA Enterprise. + websiteKey: The reCAPTCHA website key. + enterprisePayload: An optional parameter containing additional values passed + to the 'grecaptcha.enterprise.render' method along with the sitekey. + apiDomain: An optional domain address from which to load reCAPTCHA Enterprise. + This parameter should only be set if explicitly needed. + cookies: Optional cookies to be sent with the request. + proxy: Optional proxy settings for handling the task. + + Methods: + to_request: Converts the object to its corresponding request dictionary for + task execution, merging proxy settings if provided. + """ + type: str = Field(default="RecaptchaV2EnterpriseTask", frozen=True) + websiteURL: str = Field(..., description='Address of a webpage with Google reCAPTCHA Enterprise.') + websiteKey: str = Field(..., description='reCAPTCHA website key.') + enterprisePayload: Optional[str] = Field(default=None, + description='Some implementations of the reCAPTCHA Enterprise widget ' + 'may contain additional parameters that are passed to the ' + '“grecaptcha.enterprise.render” method along with the sitekey.') + apiDomain: Optional[str] = Field(default=None, + description='Domain address from which to load reCAPTCHA Enterprise. ' + 'Don\'t use a parameter if you don\'t know why it\'s needed.') + cookies: Optional[str] = Field(default=None, description='Cookies to be sent with the request.') + proxy: Optional[ProxyPayload] = Field(default=None, description='Proxy settings.') + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/recaptcha_v3.py b/capmonster_python/tasks/recaptcha_v3.py new file mode 100644 index 0000000..3dbedda --- /dev/null +++ b/capmonster_python/tasks/recaptcha_v3.py @@ -0,0 +1,36 @@ +from typing import Any, Optional + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload + + +class RecaptchaV3Task(TaskPayload): + """ + Represents a task for solving Google reCAPTCHA v3 without using a proxy. + + Attributes: + websiteURL: Address of the webpage that contains the reCAPTCHA to be solved. + websiteKey: The specific site key corresponding to the reCAPTCHA on the target website. + minScore: Defines the minimum acceptable score required for the captcha + result, ranging between 0.1 and 0.9. Setting this helps filter the quality of + the solution response. + pageAction: Specifies the widget action value, typically indicating what + the user action represents on the page. This value is set by the website owner. + + Methods: + to_request(): + Processes the instance's data into a request-ready dictionary format, including + additional proxy settings if applicable. This method ensures the final payload + is prepared as needed for sending a solve captcha request. + """ + type: str = Field(default="RecaptchaV3TaskProxyless", frozen=True) + websiteURL: str = Field(..., description='Address of a webpage with captcha.') + websiteKey: str = Field(..., description='reCAPTCHA website key.') + minScore: Optional[float] = Field(default=None, description='Minimum score of a captcha.', ge=0.1, le=0.9) + pageAction: Optional[str] = Field(default=None, + description='Widget action value. Website owner defines what user is doing on ' + 'the page through this parameter. Default value: verify') + + def to_request(self) -> dict[str, Any]: + return self.model_dump(exclude_none=True) diff --git a/capmonster_python/tasks/task.py b/capmonster_python/tasks/task.py new file mode 100644 index 0000000..2251803 --- /dev/null +++ b/capmonster_python/tasks/task.py @@ -0,0 +1,68 @@ +from abc import ABC, abstractmethod +from typing import Optional, Dict, Any, Literal + +from pydantic import BaseModel, model_validator, Field + + +class TaskPayload(BaseModel, ABC): + type: str = Field(..., description="Task type") + + @abstractmethod + def to_request(self) -> Dict[str, Any]: + """ + Converts the instance's model data into a request-compatible dictionary. + + Returns: + dict[str, Any]: A dictionary representation of the model data, formatted + for request purposes. + """ + pass + + +class ProxyPayload(BaseModel): + proxyType: Literal["http", "https", "socks4", "socks5"] = Field( + ..., description="Type of the proxy (http, https, socks4, socks5)" + ) + proxyAddress: str = Field( + ..., title="Proxy Address", + description="IPv4/IPv6 proxy IP address. Hostnames, transparent and local proxies are not allowed." + ) + proxyPort: int = Field( + ..., title="Proxy Port", + description="Port of the proxy." + ) + proxyLogin: Optional[str] = Field( + default=None, description="Username for proxy authentication." + ) + proxyPassword: Optional[str] = Field( + default=None, description="Password for proxy authentication." + ) + + @model_validator(mode="after") + def validate_fields(self) -> "ProxyPayload": + if self.proxyType and (not self.proxyAddress or not self.proxyPort): + raise ValueError("proxyAddress and proxyPort are required if proxyType is set.") + return self + + +class UserAgentPayload(BaseModel, ABC): + userAgent: Optional[str] = Field( + default=None, + description="Browser User-Agent string used to recognize captcha." + ) + + +class VanillaTaskPayload(TaskPayload, UserAgentPayload): + type: str = Field(..., description="Task type string, e.g. 'CustomTask'") + proxy: Optional[ProxyPayload] = Field(default=None, description="Proxy settings (optional)") + + model_config = { + "extra": "allow" + } + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/temu.py b/capmonster_python/tasks/temu.py new file mode 100644 index 0000000..2dfdc70 --- /dev/null +++ b/capmonster_python/tasks/temu.py @@ -0,0 +1,37 @@ +from typing import Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload, UserAgentPayload + + +class TemuTask(TaskPayload, UserAgentPayload): + """ + Represents a Temu task for CAPTCHA challenge handling. + + Attributes: + class_ (str): The class name for the task, aliased as "class" for JSON + compatibility. This is always set to "Temu" and is immutable. + websiteURL (str): The full URL of the webpage where the CAPTCHA is loaded. + cookie (str): Cookies obtained from the webpage where the CAPTCHA is + loaded. These cookies are typically required for CAPTCHA validation. + + Methods: + to_request: Converts the current task instance into a dictionary format + suitable for making a network request, including specific metadata + processing for cookies. + """ + type: str = Field(default="CustomTask", frozen=True) + class_: str = Field(default="Temu", alias="class", frozen=True) + websiteURL: str = Field(..., description="The full URL of the page where the CAPTCHA is loaded.") + cookie: str = Field(..., description="Cookies obtained from the page where the CAPTCHA is loaded.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True, by_alias=True) + cookie = base.pop("cookie") + + if "metadata" not in base: + base["metadata"] = {} + + base["metadata"]["cookie"] = cookie + return base diff --git a/capmonster_python/tasks/tendi.py b/capmonster_python/tasks/tendi.py new file mode 100644 index 0000000..5edd7f7 --- /dev/null +++ b/capmonster_python/tasks/tendi.py @@ -0,0 +1,30 @@ +from typing import Optional, Any + +from pydantic import Field + +from capmonster_python.tasks.task import TaskPayload, UserAgentPayload, ProxyPayload + + +class TenDITask(TaskPayload, UserAgentPayload): + """ + Represents a task configuration for solving a captcha using the TenDI service. + + Attributes: + class_: Represents the class name of the task. Default is "TenDI". + websiteURL: Address of the page on which the captcha is solved. + websiteKey: A unique identifier (captchaAppId) specific to the site. + proxy: Optional. Proxy settings for the task, if applicable. + """ + type: str = Field(default="CustomTask", frozen=True) + class_: str = Field(default="TenDI", alias="class", frozen=True) + websiteURL: str = Field(..., description="Address of the page on which the captcha is solved.") + websiteKey: str = Field(..., description="captchaAppId. For example websiteKey: 189123456 - is a " + "unique parameter for your site.") + proxy: Optional[ProxyPayload] = Field(..., description="Proxy settings.") + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True, by_alias=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tasks/turnstile.py b/capmonster_python/tasks/turnstile.py new file mode 100644 index 0000000..344a89f --- /dev/null +++ b/capmonster_python/tasks/turnstile.py @@ -0,0 +1,81 @@ +from typing import Optional, Dict, Any, Literal + +from pydantic import Field, model_validator + +from capmonster_python.tasks.task import TaskPayload, ProxyPayload + + +class TurnstileTask(TaskPayload): + """ + Represents a TurnstileTask used for solving captcha. + + Attributes: + websiteURL: A required string containing the page address where the captcha + is solved. + websiteKey: A required string for the Turnstile key. + pageAction: An optional string representing the action field that can be + found in the callback function to load the captcha. + data: An optional string containing the value of the data field, which can + be obtained from the cData parameter. + """ + type: str = Field(default="TurnstileTaskProxyless", frozen=True) + websiteURL: str = Field(..., description='The page address, where the captcha is solved') + websiteKey: str = Field(..., description='Turnstile key.') + pageAction: Optional[str] = Field(default=None, description='The action field that can be found in the callback ' + 'function to load the captcha') + data: Optional[str] = Field(default=None, + description='The value of the data field can be taken from the cData parameter.') + + def to_request(self) -> Dict[str, Any]: + return self.model_dump(exclude_none=True) + + +class TurnstileCloudFlareTask(TurnstileTask): + """ + Represents a Turnstile task for Cloudflare challenges. + + Attributes: + cloudflareTaskType: Specifies the Cloudflare challenge type. Expected + values are "token" or "cf_clearance". + userAgent: Browser User-Agent string, must originate from a Windows OS. + apiJsUrl: Optional captcha script URL, applicable only for "token" type tasks. + htmlPageBase64: Base64-encoded HTML page content, required for tasks + with "cf_clearance" type. + proxy: Optional proxy settings; mandatory for the "cf_clearance" type and + omitted for the "token" type. + + Raises: + ValueError: When field requirements specific to the `cloudflareTaskType` + are not satisfied. + """ + type: str = Field(default="TurnstileTask") + cloudflareTaskType: Literal["token", "cf_clearance"] = Field(default="token", + description="Type of Cloudflare challenge") + userAgent: str = Field(..., description="Browser User-Agent string (must be from Windows OS)") + apiJsUrl: Optional[str] = Field(default=None, description="Captcha script URL (only for token-based tasks)") + htmlPageBase64: Optional[str] = Field(default=None, + description="Base64-encoded HTML page content, required for cf_clearance type") + proxy: Optional[ProxyPayload] = Field( + default=None, description="Proxy settings (required if type is cf_clearance)" + ) + + @model_validator(mode="after") + def check_fields_match_type(self) -> "TurnstileCloudFlareTask": + if self.cloudflareTaskType == "token": + if not self.apiJsUrl: + raise ValueError("apiJsUrl is required for cloudflareTaskType='token'") + if self.proxy is not None: + raise ValueError("proxy must be omitted for cloudflareTaskType='token'") + elif self.cloudflareTaskType == "cf_clearance": + if not self.htmlPageBase64: + raise ValueError("htmlPageBase64 is required for cloudflareTaskType='cf_clearance'") + if self.proxy is None: + raise ValueError("proxy is required for cloudflareTaskType='cf_clearance'") + return self + + def to_request(self) -> dict[str, Any]: + base = self.model_dump(exclude_none=True) + proxy_dict = base.pop("proxy", {}) + if proxy_dict: + base.update(proxy_dict) + return base diff --git a/capmonster_python/tendi.py b/capmonster_python/tendi.py deleted file mode 100644 index 66d79bf..0000000 --- a/capmonster_python/tendi.py +++ /dev/null @@ -1,19 +0,0 @@ -from .capmonster import UserAgent - -class TenDITask(UserAgent): - def __init__(self, client_key): - super(TenDITask, self).__init__(client_key) - - def create_task(self, website_url: str, website_key: str): - data = { - "clientKey": self._client_key, - "task": { - "type": "CustomTask", - "class": "TenDI", - "websiteURL": website_url, - "websiteKey": website_key - } - } - - data, is_user_agent = self._add_user_agent(data) - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/turnstile.py b/capmonster_python/turnstile.py deleted file mode 100644 index e14e436..0000000 --- a/capmonster_python/turnstile.py +++ /dev/null @@ -1,23 +0,0 @@ -from .capmonster import Proxy, UserAgent -from typing import Union - - -class TurnstileTask(UserAgent, Proxy): - def __init__(self, client_key): - super(TurnstileTask, self).__init__(client_key) - - def create_task(self, website_url: str, website_key: str, - no_cache: bool = None): - data = { - "clientKey": self._client_key, - "task": { - "type": "TurnstileTask", - "websiteURL": website_url, - "websiteKey": website_key - } - } - data, is_user_agent = self._add_user_agent(data) - data, is_proxy = self._is_proxy_task(data) - if no_cache: - data["task"]["nocache"] = no_cache - return self._make_request("createTask", data).get("taskId") diff --git a/capmonster_python/utils.py b/capmonster_python/utils.py deleted file mode 100644 index 21c83d7..0000000 --- a/capmonster_python/utils.py +++ /dev/null @@ -1,35 +0,0 @@ -from functools import wraps - - -class CapmonsterException(Exception): - def __init__(self, error_id, error_code, error_description, *args, **kwargs): - super(CapmonsterException, self).__init__("[{}:{}]{}".format(error_code, error_id, error_description)) - self.error_description = error_description - self.error_id = error_id - self.error_code = error_code - - def __str__(self): - return "[{}] {}".format(self.error_code, self.error_description) - - -def check_response(): - def checker(f): - @wraps(f) - def wrap(*args, **kwargs): - rf = f(*args, **kwargs) - if type(rf) is dict: - if rf.get("errorId") == 0: - return rf - else: - raise CapmonsterException(error_id=rf.get("errorId"), - error_code=rf.get("errorCode"), - error_description=rf.get("errorDescription")) - else: - raise CapmonsterException(error_id=-1, - error_code="CAPMONSTER_API_ERROR", - error_description="Sometimes can be happen if capmonster_python " - "servers there is too much intensity") - - return wrap - - return checker diff --git a/docs/gatsby-config.js b/docs/gatsby-config.js deleted file mode 100644 index 77dafbe..0000000 --- a/docs/gatsby-config.js +++ /dev/null @@ -1,53 +0,0 @@ -module.exports = { - siteMetadata: { - siteTitle: `capmonster-python docs`, - defaultTitle: `capmonster-python docs`, - siteTitleShort: `capmonster-python`, - siteDescription: `capmonster-python documentation for capmonster.cloud`, - siteUrl: `https://docs.alperenn.com`, - siteAuthor: `@alperensert`, - siteImage: `/banner.png`, - siteLanguage: `en`, - themeColor: `#8257E6`, - basePath: `/`, - }, - plugins: [ - { - resolve: `@rocketseat/gatsby-theme-docs`, - options: { - configPath: `src/config`, - docsPath: `src/docs`, - yamlFilesPath: `src/yamlFiles`, - repositoryUrl: `https://github.com/jpedroschmitz/rocketdocs`, - baseDir: `examples/gatsby-theme-docs`, - gatsbyRemarkPlugins: [], - }, - }, - { - resolve: `gatsby-plugin-manifest`, - options: { - name: `Rocket Docs`, - short_name: `Rocket Docs`, - start_url: `/`, - background_color: `#ffffff`, - display: `standalone`, - icon: `static/favicon.png`, - }, - }, - `gatsby-plugin-sitemap`, - // { - // resolve: `gatsby-plugin-google-analytics`, - // options: { - // trackingId: `YOUR_ANALYTICS_ID`, - // }, - // }, - `gatsby-plugin-remove-trailing-slashes`, - { - resolve: `gatsby-plugin-canonical-urls`, - options: { - siteUrl: `https://capmonster-python.alperenn.com`, - }, - }, - `gatsby-plugin-offline`, - ], -}; diff --git a/docs/package.json b/docs/package.json deleted file mode 100644 index 62b7b02..0000000 --- a/docs/package.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "gatsby-starter-rocketdocs", - "private": true, - "version": "1.0.0", - "description": "Out of the box Gatsby Starter for creating documentation websites easily and quickly. With support for MDX, code highlight, Analytics, SEO and more", - "author": "João Pedro Schmitz (@jpedroschmitz)", - "license": "MIT", - "starter-name": "gatsby-starter-rocketdocs", - "dependencies": { - "@rocketseat/gatsby-theme-docs": "^3.2.0", - "gatsby": "^4.2.0", - "gatsby-plugin-canonical-urls": "^4.2.0", - "gatsby-plugin-google-analytics": "^4.2.0", - "gatsby-plugin-manifest": "^4.2.0", - "gatsby-plugin-offline": "^5.2.0", - "gatsby-plugin-remove-trailing-slashes": "^4.2.0", - "gatsby-plugin-sitemap": "^5.2.0", - "prop-types": "^15.7.2", - "react": "^17.0.2", - "react-dom": "^17.0.2" - }, - "devDependencies": { - "gh-pages": "^4.0.0", - "yarn-upgrade-all": "^0.7.1" - }, - "keywords": [ - "gatsby", - "gatsby-starter" - ], - "scripts": { - "build": "gatsby build --prefix-paths", - "start": "gatsby develop", - "serve": "gatsby serve", - "clean": "gatsby clean", - "deploy": "gatsby build --prefix-paths && gh-pages -d public" - } -} diff --git a/docs/src/@rocketseat/gatsby-theme-docs/text/index.mdx b/docs/src/@rocketseat/gatsby-theme-docs/text/index.mdx deleted file mode 100644 index e88e4c6..0000000 --- a/docs/src/@rocketseat/gatsby-theme-docs/text/index.mdx +++ /dev/null @@ -1,19 +0,0 @@ -# Introduction - -Capmonster is a cloud service for automatic recognition of reCAPTCHA, hCaptcha and other types of captchas. -This is the documentation of [Capmonster.cloud](https://capmonster.cloud) Python3 package. - -## Available Captcha Types - -- Image to Text -- ReCaptcha V2 -- ReCaptcha V2 Enterprise -- FunCaptcha -- HCaptcha -- ReCaptcha V3 -- GeeTest -- Turnstilke Task - -
- -[Get started now!](/installation) diff --git a/docs/src/config/sidebar.yml b/docs/src/config/sidebar.yml deleted file mode 100644 index bb4f6df..0000000 --- a/docs/src/config/sidebar.yml +++ /dev/null @@ -1,56 +0,0 @@ -# Sidebar navigation - -- label: 'Introduction' - link: '/' -- label: 'Installation' - link: '/installation' -- label: Usage - items: - - label: 'Image to Text' - link: '/usage/solve-img-to-text' - - label: 'ReCaptcha v2' - link: '/usage/solve-recap-v2' - - label: 'FunCaptcha' - link: '/usage/solve-funcap' - - label: 'HCaptcha' - link: '/usage/solve-hcaptcha' - - label: 'ReCaptcha v3' - link: '/usage/solve-recap-v3' - - label: 'GeeTest' - link: '/usage/solve-geetest' - - label: 'Turnstile' - link: '/usage/solve-turnstile' - - label: 'Proxy and User Agent' - link: '/usage/proxy-and-ua' -- label: API - items: - - label: class Capmonster - link: '/api/class-capmonster' - - label: class ImageToTextTask - link: '/api/class-img-to-text' - - label: class ReCaptchaV2Task - link: '/api/class-recap-v2' - - label: class GeeTestTask - link: '/api/class-geetest' - - label: class ReCaptchaV3Task - link: '/api/class-recap-v3' - - label: class FunCaptchaTask - link: '/api/class-funcap' - - label: class HCaptchaTask - link: '/api/class-hcap' - - label: class TurnstileTask - link: '/api/class-turnstile' - - label: class Proxy - link: '/api/class-proxy' - - label: class UserAgent - link: '/api/class-useragent' -- label: More - items: - - label: Error Types - link: /error-types - - label: No Cache - link: /no-cache - - label: Examples - link: /examples - - label: 'Github' - link: https://github.com/alperensert/capmonster_python diff --git a/docs/src/docs/api/class-capmonster.mdx b/docs/src/docs/api/class-capmonster.mdx deleted file mode 100644 index d412782..0000000 --- a/docs/src/docs/api/class-capmonster.mdx +++ /dev/null @@ -1,32 +0,0 @@ ---- -title: class Capmonster ---- - -## Methods -### get_balance -- Does not have any specific parameter, returns the current balance of specified api key. - -### get_task_result -- Get task's result. Returns a dictionary if task is ready, else returns `false` - -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|task_id|Integer|Yes|-|ID which was obtained in createTask method| - -### join_task_result -- Get task's result. Returns a dictionary if task is ready, else raise CapmonsterException with error code `MAXIMUM_TIME_EXCEED` - -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|task_id|Integer|Yes|-|ID which was obtained in createTask method| -|maximum_time|Integer|No|120| - -### join_task_result_async -- Get task's result. Returns a dictionary if task is ready, else raise CapmonsterException with error code `MAXIMUM_TIME_EXCEED` - -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|task_id|Integer|Yes|-|ID which was obtained in createTask method| -|maximum_time|Integer|No|120| - -> All task classes are inherits the _Capmonster_ class. \ No newline at end of file diff --git a/docs/src/docs/api/class-funcap.mdx b/docs/src/docs/api/class-funcap.mdx deleted file mode 100644 index e2251ab..0000000 --- a/docs/src/docs/api/class-funcap.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: class FunCaptchaTask ---- - -## Constructor -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|client_key|String|Yes|-|Your unique key to solve captchas| - -## Methods -### create_task -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|website_url|String|Yes|-|Address of a webpage with FunCaptcha| -|website_public_key|String|Yes|-|FunCaptcha website key

_(div id="funcaptcha" data-pkey="THAT_ONE")_| -|api_js_subdomain|String|No|-|A special subdomain of funcaptcha.com, from which the JS captcha widget should be loaded. Most FunCaptcha installations work from shared domains, so this option is only needed in certain rare cases.| -|data_blob|String|No|-|Additional parameter that may be required by Funcaptcha implementation.

Use this property to send "blob" value as a stringified array. See example how it may look like.
_{"\blob\":\"HERE_COMES_THE_blob_VALUE\"}_| -|cookies|Dict, List, String|No|-|Additional cookies which we must use during interaction with target page or Google

If you are pass the cookies as string, here is the format: cookiename1=cookievalue1; cookiename2=cookievalue2| -|no_cache|Boolean|No|-|You receive a token, send it to the site, but the site rejects it. Moreover, sometimes the site can accept a token, e.g. in one case out of 10 (_the percentage of success in your case may be different_)| - -### Inherited methods -- See [_Capmonster_](/api/class-capmonster) class for inherited methods. -- See [_Proxy_](/api/class-proxy) class for inherited methods. -- See [_UserAgent_](/api/class-useragent) class for inherited methods. \ No newline at end of file diff --git a/docs/src/docs/api/class-geetest.mdx b/docs/src/docs/api/class-geetest.mdx deleted file mode 100644 index 0fd3d8b..0000000 --- a/docs/src/docs/api/class-geetest.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: class GeeTestTask ---- - -## Constructor -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|client_key|String|Yes|-|Your unique key to solve captchas| - -## Methods -### create_task -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|website_url|String|Yes|-|Address of the page on which the captcha is ricognized| -|gt|String|Yes|-|The GeeTest identifier key for the domain. Static value, rarely updated.| -|challenge|String|Yes|-|A dynamic key.
Each time our API is called, we need to get a new key value. If the captcha is loaded on the page, then the challenge value is no longer valid.
It is necessary to examine the requests and find the one in which this value is returned and, before each creation of the recognition task, execute this request and parse the challenge from it.| -|api_server_subdomain|String|No|-|Optional parameter. May be required for some sites.| -|get_lib|String|No|-|Optional parameter. May be required for some sites.
Send JSON as a string.| - -### Inherited methods -- See [_Capmonster_](/api/class-capmonster) class for inherited methods. -- See [_Proxy_](/api/class-proxy) class for inherited methods. -- See [_UserAgent_](/api/class-useragent) class for inherited methods. \ No newline at end of file diff --git a/docs/src/docs/api/class-hcap.mdx b/docs/src/docs/api/class-hcap.mdx deleted file mode 100644 index fa82dc4..0000000 --- a/docs/src/docs/api/class-hcap.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: class HCaptchaTask ---- - -## Constructor -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|client_key|String|Yes|-|Your unique key to solve captchas| - -## Methods - -### create_task -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|website_url|String|Yes|-|Address of a webpage with hCaptcha| -|website_key|String|Yes|-|hCaptcha website key| -|is_invisible|Boolean|No|-|Use `True` for invisible version of hcaptcha| -|custom_data|String|No|-|Custom data that is used in some implementations of hCaptcha, mostly with isInvisible=true. In most cases you see it as rqdata inside network requests

**IMPORTANT:** You **MUST** provide an user agent if you submit captcha with data parameter. The value should match the user agent you use when interacting with the target website.| -|cookies|Dict, List, String|No|-|Additional cookies which we must use during interaction with target page or Google

If you are pass the cookies as string, here is the format: cookiename1=cookievalue1; cookiename2=cookievalue2| -|no_cache|Boolean|No|-|You receive a token, send it to the site, but the site rejects it. Moreover, sometimes the site can accept a token, e.g. in one case out of 10 (_the percentage of success in your case may be different_)| - -### Inherited methods -- See [_Capmonster_](/api/class-capmonster) class for inherited methods. -- See [_Proxy_](/api/class-proxy) class for inherited methods. -- See [_UserAgent_](/api/class-useragent) class for inherited methods. \ No newline at end of file diff --git a/docs/src/docs/api/class-img-to-text.mdx b/docs/src/docs/api/class-img-to-text.mdx deleted file mode 100644 index 7e2c4a2..0000000 --- a/docs/src/docs/api/class-img-to-text.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -title: class ImageToTextTask ---- - -## Constructor -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|client_key|String|Yes|-|Your unique key to solve captchas| - -## Methods - -### create_task -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|image_path|String|Yes *|-|Automatically encode with base64| -|base64_encoded_image|String|Yes *|-|Image body encoded in base64. Make sure to send it without line breaks and as string| -|module|String|No|-|Name of recognizing module e.g _yandex_ -|recognizing_threshold|Integer|No|-|Captcha recognition threshold with a possible value from 0 to 100. For example, if a value of 90 was sent to the system, and the problem was solved with confidence of 80, then the money for the solution will not be charged. In this case, the user will receive an ERROR_CAPTCHA_UNSOLVABLE exception| -|case|Boolean|No|-|Whether to consider case when deciding or not| -|numeric|Integer|No|0|**1** - if the captcha consists of numbers only| -|math|Boolean|No|-|**true** - captcha requires a mathematical action (e.g captcha 2 + 6 = will return the value 8)| - -### Inherited methods -See [_Capmonster_](/api/class-capmonster) class for inherited methods. \ No newline at end of file diff --git a/docs/src/docs/api/class-proxy.mdx b/docs/src/docs/api/class-proxy.mdx deleted file mode 100644 index 39303ad..0000000 --- a/docs/src/docs/api/class-proxy.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -title: class Proxy ---- - -## Methods -### set_proxy -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|proxy_type|String|Yes|-|Type of the proxy

**http** - usual http/https proxy
**https** - try this only if "http" doesn't work (required by some custom proxy servers)
**socks4** - socks4 proxy
**socks5** - socks5 proxy
| -|proxy_address|String|Yes|-|Proxy IP address IPv4/IPv6. Not allowed to use:

| -|proxy_port|Integer|Yes|-|Proxy port| -|proxy_login|String|No *|-|Login for proxy which requires authorizaiton (basic)| -|proxy_password|String|No *|-|Proxy password| - -\* If your proxy isn't authorized by IP you must pass the both of them. But the proxy is authorized by IP, then be sure to add **116.203.55.208** to the white list. - -### method disable_proxy -Doesn't have any specific parameters, it's disables and clear the proxy. \ No newline at end of file diff --git a/docs/src/docs/api/class-recap-v2.mdx b/docs/src/docs/api/class-recap-v2.mdx deleted file mode 100644 index 17ebd76..0000000 --- a/docs/src/docs/api/class-recap-v2.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: class ReCaptchaV2Task ---- - -## Constructor -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|client_key|String|Yes|-|Your unique key to solve captchas| - -## Methods -### create_task -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|website_url|String|Yes|-|Address of a webpage with Google ReCaptcha| -|website_key|String|Yes|-|Recaptcha website key _(div class="g-recaptcha" data-sitekey="THAT_ONE")_| -|cookies|Dict, List, String|No|-|Additional cookies which we must use during interaction with target page or Google

If you are pass the cookies as string, here is the format: cookiename1=cookievalue1; cookiename2=cookievalue2| -|recaptcha_s_value|String|No|-|Some custom implementations may contain additional "data-s" parameter in ReCaptcha2 div, which is in fact a one-time token and must be grabbed every time you want to solve a ReCaptcha2.

_div class="g-recaptcha" data-sitekey="some sitekey" data-s="THIS_ONE"_| -|no_cache|Boolean|No|-|You receive a token, send it to the site, but the site rejects it. Moreover, sometimes the site can accept a token, e.g. in one case out of 10 (_the percentage of success in your case may be different_)| - -### Inherited methods -- See [_Capmonster_](/api/class-capmonster) class for inherited methods. -- See [_Proxy_](/api/class-proxy) class for inherited methods. -- See [_UserAgent_](/api/class-useragent) class for inherited methods. \ No newline at end of file diff --git a/docs/src/docs/api/class-recap-v3.mdx b/docs/src/docs/api/class-recap-v3.mdx deleted file mode 100644 index fc9185c..0000000 --- a/docs/src/docs/api/class-recap-v3.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: class ReCaptchaV3Task ---- - -## Constructor -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|client_key|String|Yes|-|Your unique key to solve captchas| - -## Methods - -### create_task -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|website_url|String|Yes|-|Address of a webpage with Google ReCaptcha| -|website_key|String|Yes|-|Recaptcha website key

_(google.com/recaptcha/api.js?render="THAT_ONE")_| -|minimum_score|Float (double)|No|0.3|Value from 0.1 to 0.9| -|page_action|String|No|verify|Widget action value. Website owner defines what user is doing on the page through this parameter.

_e.g. grecaptcha.execute('site_key', {action:'login_test'})_| -|no_cache|Boolean|No|-|You receive a token, send it to the site, but the site rejects it. Moreover, sometimes the site can accept a token, e.g. in one case out of 10 (_the percentage of success in your case may be different_)| - -### Inherited methods -See [_Capmonster_](/api/class-capmonster) class for inherited methods. \ No newline at end of file diff --git a/docs/src/docs/api/class-turnstile.mdx b/docs/src/docs/api/class-turnstile.mdx deleted file mode 100644 index b68c0a2..0000000 --- a/docs/src/docs/api/class-turnstile.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: class TurnstileTask ---- - -## Constructor -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|client_key|String|Yes|-|Your unique key to solve captchas| - -## Methods -### create_task -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|website_url|String|Yes|-|Address of a webpage with Google ReCaptcha Enterprise| -|website_key|String|Yes|-|Turnstile key| -|no_cache|Boolean|No|-|You receive a token, send it to the site, but the site rejects it. Moreover, sometimes the site can accept a token, e.g. in one case out of 10 (_the percentage of success in your case may be different_)| - -### Inherited methods -- See [_Capmonster_](/api/class-capmonster) class for inherited methods. -- See [_Proxy_](/api/class-proxy) class for inherited methods. \ No newline at end of file diff --git a/docs/src/docs/api/class-useragent.mdx b/docs/src/docs/api/class-useragent.mdx deleted file mode 100644 index 99c44ab..0000000 --- a/docs/src/docs/api/class-useragent.mdx +++ /dev/null @@ -1,13 +0,0 @@ ---- -title: class UserAgent ---- - -## Methods - -### set_user_agent -|Parameter|Type|Required|Default value|Purpose| -|---------|----|--------|-------------|-------| -|user_agent|String|Yes|-|Add user agent to task| - -### reset_user_agent -Doesn't have any specific parameters, it's disables and clear the user agent. \ No newline at end of file diff --git a/docs/src/docs/error-types.mdx b/docs/src/docs/error-types.mdx deleted file mode 100644 index 0743350..0000000 --- a/docs/src/docs/error-types.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Error Types ---- - -## Error List -|Error Code|Error Description| -|----------|-----------------| -|ERROR_KEY_DOES_NOT_EXIST|Account authorization key not found in the system or has incorrect format (length is not)| -|ERROR_ZERO_CAPTCHA_FILESIZE|The size of the captcha you are uploading is less than 100 bytes.| -|ERROR_TOO_BIG_CAPTCHA_FILESIZE|The size of the captcha you are uploading is more than 50,000 bytes.| -|ERROR_ZERO_BALANCE|Account has zero balance| -|ERROR_IP_NOT_ALLOWED|Request with current account key is not allowed from your IP| -|ERROR_CAPTCHA_UNSOLVABLE|This type of captchas is not supported by the service or the image does not contain an answer, perhaps it is too noisy. It could also mean that the image is corrupted or was incorrectly rendered.| -|ERROR_NO_SUCH_CAPCHA_ID, WRONG_CAPTCHA_ID|The captcha that you are requesting was not found. Make sure you are requesting a status update only within 5 minutes of uploading.| -|CAPTCHA_NOT_READY|The captcha has not yet been solved| -|ERROR_IP_BANNED|You have exceeded the limit of requests with the wrong api key, check the correctness of your api key in the control panel and after some time, try again| -|ERROR_NO_SUCH_METHOD|This method is not supported or empty| -|ERROR_TOO_MUCH_REQUESTS|You have exceeded the limit of requests to receive an answer for one task| -|ERROR_MAXIMUM_TIME_EXCEED|Captcha response could not be received at the specified time| -|ERROR_NOTHING_GIVEN|You have to give image_path or base64_encoded_image at least| - -### Explanation for ERROR_IP_BANNED -If you get this error, you can see the reason from [here](https://capmonster.cloud/BanEvents/History). - -The main reasons for getting a ban: -- **KeyDoesntExist:** Multiple requests without a key or with an invalid key -- **ZeroBalance:** Multiple requests with zero balance -- **WrongTaskId:** When exceeding the limit of the 120 requests per task / 1 min -- **BadProxy:** multiple requests with a banned proxy - -> Note: Please do not forget, users receive a ban for repeated this actions for a limited period of time. - -#### When will the ban removed? -The duration of the block is **30 minutes**, _provided that you do not continue to break the rules described above_. \ No newline at end of file diff --git a/docs/src/docs/examples.mdx b/docs/src/docs/examples.mdx deleted file mode 100644 index 7bfd73a..0000000 --- a/docs/src/docs/examples.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Examples ---- - -To run the examples in your local machine, please do the followings: - -## Steps - -1. Create a folder and clone the repository - - ```sh - mkdir capmonster-python - cd capmonster-python - git init - git clone https://github.com/alperensert/capmonster_python.git - ``` -2. Go to examples folder and install the requirements (examples only) - - ```sh - cd examples - pip install -r requirements.txt - ``` -3. Then set 2 environment variables for correctly run the examples, _API_KEY_ and _HEADLESS_ - 1. Api key is your unique key to solve captchas - 2. Headless is needed for running selenium examples. - -4. Now you are ready to run examples, e.g. - - ```sh - python recaptchav2_selenium.py - ``` - -> Note: If you want to run selenium examples, Firefox must be installed - -- All examples are [here](https://github.com/alperensert/capmonster_python/tree/master/tests) diff --git a/docs/src/docs/installation.mdx b/docs/src/docs/installation.mdx deleted file mode 100644 index 66da6d8..0000000 --- a/docs/src/docs/installation.mdx +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: 'Installation' -description: Learn how to install package ---- - -```bash -pip install capmonster-python -``` - -> Please do not forget. Package is only compatible with Python >=3 versions. diff --git a/docs/src/docs/no-cache.mdx b/docs/src/docs/no-cache.mdx deleted file mode 100644 index 32eaa7d..0000000 --- a/docs/src/docs/no-cache.mdx +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: No Cache ---- - -This page is explanation of _no_cache_ parameter. - -## What if the site only accepts a portion of the tokens from Capmonster.cloud? -You receive a token from Capmonster Cloud, send it to the site, but the site rejects it. Moreover, sometimes the site can accept a token, for example, in one case out of 10 (the percentage of success in your case may be different). - -In this case, the `no_cache` parameter can help you. - -## Tasks that support this parameter -- Recaptcha v2 -- Recaptcha v3 -- Funcaptcha -- HCaptcha \ No newline at end of file diff --git a/docs/src/docs/usage/proxy-and-ua.mdx b/docs/src/docs/usage/proxy-and-ua.mdx deleted file mode 100644 index 02b6415..0000000 --- a/docs/src/docs/usage/proxy-and-ua.mdx +++ /dev/null @@ -1,79 +0,0 @@ ---- -title: Proxy and User Agent -description: Learn how to implement proxy and user agent to a task ---- - -You can use proxy and user agent in supported tasks. - -## Supported task list -- Recaptcha v2 -- Fun captcha -- HCaptcha -- GeeTest -- Turnstile - -> For GeeTest and HCaptcha, proxies with IP authorization are not yet supported.
-> For others, if the proxy is authorized by IP, then be sure to add **116.203.55.208** to the white list. - -## ReCaptcha v2 Usage -```py title=recap_v2_proxy.py -from capmonster_python import RecaptchaV2Task - -capmonster = RecaptchaV2Task("API_KEY") -capmonster.set_proxy("http", "8.8.8.8", 8080) -capmonster.set_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0") -task_id = capmonster.create_task("website_url", "website_key") -result = capmonster.join_task_result(task_id) -print(result.get("gRecaptchaResponse")) -``` - -## FunCaptcha Usage -```py title=funcap_proxy.py -from capmonster_python import FuncaptchaTask - -capmonster = FuncaptchaTask("API_KEY") -capmonster.set_proxy("http", "8.8.8.8", 8080) -capmonster.set_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0") -task_id = capmonster.create_task("website_url", "website_public_key") -result = capmonster.join_task_result(task_id) -print(result.get("token")) -``` - -## HCaptcha Usage -```py title=hcap_proxy.py -from capmonster_python import HCaptchaTask - -capmonster = HCaptchaTask("API_KEY") -capmonster.set_proxy("http", "8.8.8.8", 8080) -capmonster.set_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0") -task_id = capmonster.create_task("website_url", "website_key") -result = capmonster.join_task_result(task_id) -print(result.get("gRecaptchaResponse")) -``` - -## GeeTest Usage -```py title=geetest_proxy.py -from capmonster_python import GeeTestTask - -capmonster = GeeTestTask("API_KEY") -capmonster.set_proxy("http", "8.8.8.8", 8080) -capmonster.set_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0") -task_id = capmonster.create_task("website_url", "gt", "challenge") -result= capmonster.join_task_result(task_id) -print(result.get("challenge")) -print(result.get("seccode")) -print(result.get("validate")) -``` - -## Turnstile Usage -```py title=turnstile_proxy.py -from capmonster_python import TurnstileTask - -capmonster = TurnstileTask("API_KEY") -capmonster.set_proxy("http", "8.8.8.8", 8080) -capmonster.set_user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:91.0) Gecko/20100101 Firefox/91.0") -task_id = capmonster.create_task("website_url", "website_key") -result = capmonster.join_task_result(task_id) -print(result.get("token")) -``` - diff --git a/docs/src/docs/usage/solve-funcap.mdx b/docs/src/docs/usage/solve-funcap.mdx deleted file mode 100644 index ee5280b..0000000 --- a/docs/src/docs/usage/solve-funcap.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Solve FunCaptcha -description: Learn how to solve funcaptcha ---- - -> First of all, you need an API key to work. You can get one from [here](https://capmonster.cloud) - -## What you need -1. Website's public key -2. Website url - -### Get public website key -Mostly, you can find the key in _data-pkey_ attribute. -```html -
-```` - -## Solve - -```py title=solve_funcaptcha.py -from capmonster_python import FuncaptchaTask - -capmonster = FuncaptchaTask("API_KEY") -task_id = capmonster.create_task("website_url", "website_public_key") -result = capmonster.join_task_result(task_id) -print(result.get("token")) -```` \ No newline at end of file diff --git a/docs/src/docs/usage/solve-geetest.mdx b/docs/src/docs/usage/solve-geetest.mdx deleted file mode 100644 index 953d6a2..0000000 --- a/docs/src/docs/usage/solve-geetest.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -title: Solve GeeTest -description: Learn how to solve GeeTest ---- - -> First of all, you need an API key to work. You can get one from [here](https://capmonster.cloud) - -## What you need -1. GeeTest identifier key (a.k.a. _gt_) -2. Challenge key -2. Website url - -### How to get you needs? -- The gt, challenge and geetestApiServerSubdomain parameters are most often found inside the initGeetest JavaScript function. -- Also you can see in the HTML code of the page. -You can find it in the **script** block, which appears after the page is fully loaded in the browser. -![geetest](/geetest.jpg) - -## Solve - -```py title=solve_geetest.py -from capmonster_python import GeeTestTask - -capmonster = GeeTestTask("API_KEY") -task_id = capmonster.create_task("website_url", "gt", "challenge") -result= capmonster.join_task_result(task_id) -print(result.get("challenge")) -print(result.get("seccode")) -print(result.get("validate")) -``` - -## Important Note -> Keep in mind, the _gt_ challenge is rarely updated. -> But challenge value is always changing when everytime you see a GeeTest Captcha. \ No newline at end of file diff --git a/docs/src/docs/usage/solve-hcaptcha.mdx b/docs/src/docs/usage/solve-hcaptcha.mdx deleted file mode 100644 index f023807..0000000 --- a/docs/src/docs/usage/solve-hcaptcha.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Solve HCaptcha -description: Learn how to solve HCaptcha ---- - -> First of all, you need an API key to work. You can get one from [here](https://capmonster.cloud) - -## What you need -1. Website key -2. Website url - -## Solve - -```py title=solve_hcaptcha.py -from capmonster_python import HCaptchaTask - -capmonster = HCaptchaTask("API_KEY") -task_id = capmonster.create_task("website_url", "website_key") -result = capmonster.join_task_result(task_id) -print(result.get("gRecaptchaResponse")) -``` \ No newline at end of file diff --git a/docs/src/docs/usage/solve-img-to-text.mdx b/docs/src/docs/usage/solve-img-to-text.mdx deleted file mode 100644 index 3b5241e..0000000 --- a/docs/src/docs/usage/solve-img-to-text.mdx +++ /dev/null @@ -1,37 +0,0 @@ ---- -title: Solve Image to Text Captcha -description: Learn how to solve image to text captchas. ---- - -> First of all, you need an API key to work. You can get one from [here](https://capmonster.cloud) - -Image to Text Captcha's can be send with two different ways. - -### Send image with ImagePath - -If you have the image on your local path, or if you can download it, use _image_path_ way. - -```py title=image_to_text_with_image_path.py -from capmonster_python import ImageToTextTask - -capmonster = ImageToTextTask("API_KEY") -task_id = capmonster.create_task(image_path="img.png") -result = capmonster.join_task_result(task_id) -print(result.get("text")) -``` - -### Send image as Base64 Encoded - -If you have to handle the captchas real-time, -you can download the image and convert it to Base64. - -```python title=image_to_text_base64_encoded.py -from capmonster_python import ImageToTextTask - -capmonster = ImageToTextTask("API_KEY") -task_id = capmonster.create_task(base64_encoded_image="base64encodedimage_as_string") -result = capmonster.join_task_result(task_id) -print(result.get("text")) -``` - -> Make sure to send it without line breaks while using Base64 Encoded way. \ No newline at end of file diff --git a/docs/src/docs/usage/solve-recap-v2.mdx b/docs/src/docs/usage/solve-recap-v2.mdx deleted file mode 100644 index 509db3c..0000000 --- a/docs/src/docs/usage/solve-recap-v2.mdx +++ /dev/null @@ -1,29 +0,0 @@ ---- -title: Solve ReCaptcha V2 -description: Learn how to solve re-captcha v2 ---- - -> First of all, you need an API key to work. You can get one from [here](https://capmonster.cloud) - -ReCaptcha v2 is a captcha service implementation from Google. - -## What you need -1. Website's captcha key -2. Website url - -### Get website key -Mostly, you can find website key inside the _g-recaptcha_ div. -```html -
-```` - -## Solve - -```py title=solve_recaptchav2.py -from capmonster_python import RecaptchaV2Task - -capmonster = RecaptchaV2Task("API_KEY") -task_id = capmonster.create_task("website_url", "website_key") -result = capmonster.join_task_result(task_id) -print(result.get("gRecaptchaResponse")) -```` \ No newline at end of file diff --git a/docs/src/docs/usage/solve-recap-v3.mdx b/docs/src/docs/usage/solve-recap-v3.mdx deleted file mode 100644 index 081eca5..0000000 --- a/docs/src/docs/usage/solve-recap-v3.mdx +++ /dev/null @@ -1,27 +0,0 @@ ---- -title: Solve ReCaptcha v3 -description: Learn how to solve ReCaptcha V3 ---- - -> First of all, you need an API key to work. You can get one from [here](https://capmonster.cloud) - -## What you need -1. Website key -2. Website url - -### Get website key -Mostly, you can find the site key in _api.js_'s query parameters. -```html -