Spaces:
Paused
Paused
| from __future__ import absolute_import | |
| import requests | |
| try: | |
| from urlparse import urlparse | |
| except ImportError: | |
| from urllib.parse import urlparse | |
| from ..exceptions import ( | |
| CaptchaServiceUnavailable, | |
| CaptchaAPIError, | |
| CaptchaTimeout, | |
| CaptchaParameter, | |
| CaptchaBadJobID, | |
| CaptchaReportError | |
| ) | |
| try: | |
| import polling2 | |
| except ImportError: | |
| raise ImportError("Please install the python module 'polling2' via pip") | |
| from . import Captcha | |
| class captchaSolver(Captcha): | |
| def __init__(self): | |
| super(captchaSolver, self).__init__('2captcha') | |
| self.host = 'https://2captcha.com' | |
| self.session = requests.Session() | |
| self.captchaType = { | |
| 'reCaptcha': 'userrecaptcha', | |
| 'hCaptcha': 'hcaptcha', | |
| 'turnstile': 'turnstile' | |
| } | |
| # ------------------------------------------------------------------------------- # | |
| def checkErrorStatus(response, request_type): | |
| if response.status_code in [500, 502]: | |
| raise CaptchaServiceUnavailable(f'2Captcha: Server Side Error {response.status_code}') | |
| errors = { | |
| 'in.php': { | |
| "ERROR_WRONG_USER_KEY": "You've provided api_key parameter value is in incorrect format, it should contain 32 symbols.", | |
| "ERROR_KEY_DOES_NOT_EXIST": "The api_key you've provided does not exists.", | |
| "ERROR_ZERO_BALANCE": "You don't have sufficient funds on your account.", | |
| "ERROR_PAGEURL": "pageurl parameter is missing in your request.", | |
| "ERROR_NO_SLOT_AVAILABLE": | |
| "No Slots Available.\nYou can receive this error in two cases:\n" | |
| "1. If you solve ReCaptcha: the queue of your captchas that are not distributed to workers is too long. " | |
| "Queue limit changes dynamically and depends on total amount of captchas awaiting solution and usually it's between 50 and 100 captchas.\n" | |
| "2. If you solve Normal Captcha: your maximum rate for normal captchas is lower than current rate on the server." | |
| "You can change your maximum rate in your account's settings.", | |
| "ERROR_IP_NOT_ALLOWED": "The request is sent from the IP that is not on the list of your allowed IPs.", | |
| "IP_BANNED": "Your IP address is banned due to many frequent attempts to access the server using wrong authorization keys.", | |
| "ERROR_BAD_TOKEN_OR_PAGEURL": | |
| "You can get this error code when sending ReCaptcha V2. " | |
| "That happens if your request contains invalid pair of googlekey and pageurl. " | |
| "The common reason for that is that ReCaptcha is loaded inside an iframe hosted on another domain/subdomain.", | |
| "ERROR_GOOGLEKEY": | |
| "You can get this error code when sending ReCaptcha V2. " | |
| "That means that sitekey value provided in your request is incorrect: it's blank or malformed.", | |
| "MAX_USER_TURN": "You made more than 60 requests within 3 seconds.Your account is banned for 10 seconds. Ban will be lifted automatically." | |
| }, | |
| 'res.php': { | |
| "ERROR_CAPTCHA_UNSOLVABLE": | |
| "We are unable to solve your captcha - three of our workers were unable solve it " | |
| "or we didn't get an answer within 90 seconds (300 seconds for ReCaptcha V2). " | |
| "We will not charge you for that request.", | |
| "ERROR_WRONG_USER_KEY": "You've provided api_key parameter value in incorrect format, it should contain 32 symbols.", | |
| "ERROR_KEY_DOES_NOT_EXIST": "The api_key you've provided does not exists.", | |
| "ERROR_WRONG_ID_FORMAT": "You've provided captcha ID in wrong format. The ID can contain numbers only.", | |
| "ERROR_WRONG_CAPTCHA_ID": "You've provided incorrect captcha ID.", | |
| "ERROR_BAD_DUPLICATES": | |
| "Error is returned when 100% accuracy feature is enabled. " | |
| "The error means that max numbers of tries is reached but min number of matches not found.", | |
| "REPORT_NOT_RECORDED": "Error is returned to your complain request if you already complained lots of correctly solved captchas.", | |
| "ERROR_IP_ADDRES": | |
| "You can receive this error code when registering a pingback (callback) IP or domain." | |
| "That happes if your request is coming from an IP address that doesn't match the IP address of your pingback IP or domain.", | |
| "ERROR_TOKEN_EXPIRED": "You can receive this error code when sending GeeTest. That error means that challenge value you provided is expired.", | |
| "ERROR_EMPTY_ACTION": "Action parameter is missing or no value is provided for action parameter." | |
| } | |
| } | |
| rPayload = response.json() | |
| if rPayload.get('status') == 0 and rPayload.get('request') in errors.get(request_type): | |
| raise CaptchaAPIError( | |
| f"{rPayload['request']} {errors.get(request_type).get(rPayload['request'])}" | |
| ) | |
| # ------------------------------------------------------------------------------- # | |
| def reportJob(self, jobID): | |
| if not jobID: | |
| raise CaptchaBadJobID( | |
| "2Captcha: Error bad job id to request Captcha." | |
| ) | |
| def _checkRequest(response): | |
| self.checkErrorStatus(response, 'res.php') | |
| if response.ok and response.json().get('status') == 1: | |
| return response | |
| return None | |
| response = polling2.poll( | |
| lambda: self.session.get( | |
| f'{self.host}/res.php', | |
| params={ | |
| 'key': self.api_key, | |
| 'action': 'reportbad', | |
| 'id': jobID, | |
| 'json': '1' | |
| }, | |
| timeout=30 | |
| ), | |
| check_success=_checkRequest, | |
| step=5, | |
| timeout=180 | |
| ) | |
| if response: | |
| return True | |
| else: | |
| raise CaptchaReportError( | |
| "2Captcha: Error - Failed to report bad Captcha solve." | |
| ) | |
| # ------------------------------------------------------------------------------- # | |
| def requestJob(self, jobID): | |
| if not jobID: | |
| raise CaptchaBadJobID("2Captcha: Error bad job id to request Captcha.") | |
| def _checkRequest(response): | |
| self.checkErrorStatus(response, 'res.php') | |
| if response.ok and response.json().get('status') == 1: | |
| return response | |
| return None | |
| response = polling2.poll( | |
| lambda: self.session.get( | |
| f'{self.host}/res.php', | |
| params={ | |
| 'key': self.api_key, | |
| 'action': 'get', | |
| 'id': jobID, | |
| 'json': '1' | |
| }, | |
| timeout=30 | |
| ), | |
| check_success=_checkRequest, | |
| step=5, | |
| timeout=180 | |
| ) | |
| if response: | |
| return response.json().get('request') | |
| else: | |
| raise CaptchaTimeout( | |
| "2Captcha: Error failed to solve Captcha." | |
| ) | |
| # ------------------------------------------------------------------------------- # | |
| def requestSolve(self, captchaType, url, siteKey): | |
| def _checkRequest(response): | |
| self.checkErrorStatus(response, 'in.php') | |
| if response.ok and response.json().get("status") == 1 and response.json().get('request'): | |
| return response | |
| return None | |
| data = { | |
| 'key': self.api_key, | |
| 'pageurl': url, | |
| 'json': 1, | |
| 'soft_id': 2905 | |
| } | |
| data.update({ | |
| 'method': self.captchaType[captchaType], | |
| 'googlekey' if captchaType == 'reCaptcha' else 'sitekey': siteKey | |
| }) | |
| if self.proxy: | |
| data.update({ | |
| 'proxy': self.proxy, | |
| 'proxytype': self.proxyType | |
| }) | |
| response = polling2.poll( | |
| lambda: self.session.post( | |
| f'{self.host}/in.php', | |
| data=data, | |
| allow_redirects=False, | |
| timeout=30 | |
| ), | |
| check_success=_checkRequest, | |
| step=5, | |
| timeout=180 | |
| ) | |
| if response: | |
| return response.json().get('request') | |
| else: | |
| raise CaptchaBadJobID( | |
| '2Captcha: Error no job id was returned.' | |
| ) | |
| # ------------------------------------------------------------------------------- # | |
| def getCaptchaAnswer(self, captchaType, url, siteKey, captchaParams): | |
| jobID = None | |
| if not captchaParams.get('api_key'): | |
| raise CaptchaParameter( | |
| "2Captcha: Missing api_key parameter." | |
| ) | |
| self.api_key = captchaParams.get('api_key') | |
| if captchaParams.get('proxy') and not captchaParams.get('no_proxy'): | |
| hostParsed = urlparse(captchaParams.get('proxy', {}).get('https')) | |
| if not hostParsed.scheme: | |
| raise CaptchaParameter('Cannot parse proxy correctly, bad scheme') | |
| if not hostParsed.netloc: | |
| raise CaptchaParameter('Cannot parse proxy correctly, bad netloc') | |
| self.proxyType = hostParsed.scheme | |
| self.proxy = hostParsed.netloc | |
| else: | |
| self.proxy = None | |
| try: | |
| jobID = self.requestSolve(captchaType, url, siteKey) | |
| return self.requestJob(jobID) | |
| except polling2.TimeoutException: | |
| try: | |
| if jobID: | |
| self.reportJob(jobID) | |
| except polling2.TimeoutException: | |
| raise CaptchaTimeout( | |
| f"2Captcha: Captcha solve took to long and also failed reporting the job the job id {jobID}." | |
| ) | |
| raise CaptchaTimeout( | |
| f"2Captcha: Captcha solve took to long to execute job id {jobID}, aborting." | |
| ) | |
| # ------------------------------------------------------------------------------- # | |
| captchaSolver() | |