Skip to content

Commit

Permalink
Merge pull request #18 from Matthew17-21/py-improvements
Browse files Browse the repository at this point in the history
1.4.1
  • Loading branch information
Matthew17-21 committed Sep 4, 2023
2 parents b8fbf82 + 8adc1ea commit 5be7772
Show file tree
Hide file tree
Showing 10 changed files with 211 additions and 143 deletions.
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,16 @@ def main():
- **[2Captcha](https://www.2captcha.com/)**
- **[Anticaptcha](https://www.anti-captcha.com/)**
- **[Capsolver](https://www.capsolver.com/)**
- **[CaptchaAI](https://captchaai.com/)**

### Site-Specific Support:
| Site | Site ID |Captcha Types Supported | Task Types Supported|
| :-------------: |:-------------:| :-----:| :-----:|
| Capmonster | captchatools.CapmonsterSite| Image captchas,<br/> Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | ImageToTextTask,<br/>NoCaptchaTask,<br/> NoCaptchaTaskProxyless,<br/> RecaptchaV3TaskProxyless,<br />HCaptchaTaskProxyless |
| Anticaptcha | captchatools.AnticaptchaSite| Image captchas,<br/> Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | ImageToTextTask,<br/> RecaptchaV2Task<br/> RecaptchaV2TaskProxyless,<br />RecaptchaV3TaskProxyless,<br />HCaptchaTaskProxyless |
| 2Captcha | captchatools.TwoCaptchaSite| Image captchas,<br/> Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | - |
| Capsolver | captchatools.CapsolverSite| Image captchas,<br/> Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | - |
| CaptchaAI | captchatools.CaptchaAISite| Image captchas,<br/> Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | - |



Expand All @@ -145,14 +148,19 @@ from captchatools import new_harvester, exceptions as captchaExceptions,
def main():
try:
harvester = new_harvester()
token harvester.get_token()
token = harvester.get_token()
except captchaExceptions.NoHarvesterException:
print("I need to set my captcha harvester!")
```



# Changelog
### 1.4.1
##### What's new
1. Added CaptchaAI
2. Removed internal redundant code
3. Fix creating a new harvester if pass in the ID
### 1.3.0
##### What's new
1. Get Balance Support
Expand Down
13 changes: 11 additions & 2 deletions captchatools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,17 @@
1 = Capmonster
2 = Anticaptcha
3 = 2captcha
4 = Capsolver
5 = CaptchaAI
'''
__version__ = "2.0.0"
__version__ = "1.4.1"
__author__ = "Matthew17-21"
__license__ = "MIT"

from abc import ABC, abstractmethod
from typing import Optional
from . import exceptions as captchaExceptions
from warnings import warn
class Harvester(ABC):
'''
Represents a captcha harvester.
Expand Down Expand Up @@ -72,8 +75,11 @@ def new_harvester(**kwargs) -> Harvester:
from .anticaptcha import Anticaptcha
from .capmonster import Capmonster
from .capsolver import Capsolver
from .captchaai import CaptchaAI

site = kwargs.get("solving_site","").lower()
site = kwargs.get("solving_site","")
if isinstance(site, str):
site = site.lower()
if site == 1 or site == "capmonster":
return Capmonster(**kwargs)
elif site == 2 or site == "anticaptcha":
Expand All @@ -82,9 +88,12 @@ def new_harvester(**kwargs) -> Harvester:
return Twocap(**kwargs)
elif site == 4 or site == "capsolver":
return Capsolver(**kwargs)
elif site == 5 or site == "captchaai":
return CaptchaAI(**kwargs)
raise captchaExceptions.NoHarvesterException("No solving site selected")


# Just for backward compatibility
def captcha_harvesters(**kwargs) -> Harvester:
warn('This function is deprecated. Use the `new_harvester()` function', DeprecationWarning, stacklevel=2)
return new_harvester(**kwargs)
32 changes: 5 additions & 27 deletions captchatools/anticaptcha.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from . import Harvester
from . import exceptions as captchaExceptions
from .errors import ErrCodeToException
import requests
from typing import Optional
import time
Expand All @@ -13,7 +13,7 @@ def get_balance(self) -> float:
try:
resp = requests.post("https://api.anti-captcha.com/getBalance", json=payload ,timeout=20).json()
if resp["errorId"] == 1: # Means there was an error
self.check_error(resp["errorCode"])
ErrCodeToException("Anticaptcha", resp["errorCode"])
return float(resp["balance"])
except requests.RequestException:
pass
Expand Down Expand Up @@ -86,7 +86,7 @@ def __get_id(self,**kwargs):
try:
resp = requests.post(BASEURL + "/createTask " , json=payload, timeout=20).json()
if resp["errorId"] != 0: # Means there was an error:
self.check_error(resp["errorCode"])
ErrCodeToException("Anticaptcha", resp["errorCode"])
return resp["taskId"]
except (requests.RequestException, KeyError):
pass
Expand All @@ -97,7 +97,7 @@ def __get_answer(self,task_id:int):
try:
response = requests.post(BASEURL + "/getTaskResult",json=payload,timeout=20,).json()
if response["errorId"] != 0: # Means there was an error
self.check_error(response["errorId"])
ErrCodeToException("Anticaptcha", response["errorId"])
if response["status"] == "processing":
time.sleep(3)
continue
Expand All @@ -106,26 +106,4 @@ def __get_answer(self,task_id:int):
else:
return response["solution"]["gRecaptchaResponse"]
except (requests.RequestException, KeyError):
pass

@staticmethod
def check_error(error_code):
if error_code == "ERROR_ZERO_BALANCE":
raise captchaExceptions.NoBalanceException()
elif error_code == "ERROR_WRONG_GOOGLEKEY":
raise captchaExceptions.WrongSitekeyException()
elif error_code == "ERROR_WRONG_USER_KEY" or error_code == "ERROR_KEY_DOES_NOT_EXIST":
raise captchaExceptions.WrongAPIKeyException()
elif error_code == "ERROR_TOO_BIG_CAPTCHA_FILESIZE":
raise captchaExceptions.CaptchaIMGTooBig()
elif error_code == "ERROR_PAGEURL":
raise captchaExceptions.TaskDetails(f"Error: {error_code}")
elif error_code == "MAX_USER_TURN" or error_code == "ERROR_NO_SLOT_AVAILABLE":
raise captchaExceptions.NoSlotAvailable("No slot available")
elif error_code == "ERROR_IP_NOT_ALLOWED" or error_code == "IP_BANNED" or error_code == "ERROR_IP_BLOCKED":
return captchaExceptions.Banned(error_code)
elif error_code == "ERROR_ZERO_CAPTCHA_FILESIZE" or error_code == "ERROR_UPLOAD" or \
error_code == "ERROR_CAPTCHAIMAGE_BLOCKED" or error_code == "ERROR_IMAGE_TYPE_NOT_SUPPORTED" or \
error_code == "ERROR_WRONG_FILE_EXTENSION":
raise captchaExceptions.CaptchaImageError(error_code)
else: raise captchaExceptions.UnknownError(f"Error returned from 2captcha: {error_code}")
pass
32 changes: 5 additions & 27 deletions captchatools/capmonster.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import exceptions as captchaExceptions
from .errors import ErrCodeToException
from . import Harvester
import time
import requests
Expand All @@ -18,7 +18,7 @@ def get_balance(self) -> float:
try:
resp = requests.post(BASEURL + "/getBalance", json=payload ,timeout=20).json()
if resp["errorId"] == 1: # Means there was an error
self.check_error(resp["errorCode"])
ErrCodeToException("Capmonster", resp["errorCode"])
return float(resp["balance"])
except requests.RequestException:
pass
Expand Down Expand Up @@ -91,7 +91,7 @@ def __get_id(self,**kwargs):
try:
resp = requests.post(BASEURL + "/createTask" , json=payload, timeout=20).json()
if resp["errorId"] != 0: # Means there was an error:
self.check_error(resp["errorCode"])
ErrCodeToException("Capmonster", resp["errorCode"])
return resp["taskId"]
except (requests.RequestException, KeyError):
pass
Expand All @@ -102,7 +102,7 @@ def __get_answer(self,task_id:int):
try:
response = requests.post(BASEURL + "/getTaskResult",json=payload,timeout=20,).json()
if response["errorId"] != 0: # Means there was an error
self.check_error(response["errorCode"])
ErrCodeToException("Capmonster", response["errorCode"])
if response["status"] == "processing":
time.sleep(3)
continue
Expand All @@ -111,26 +111,4 @@ def __get_answer(self,task_id:int):
else:
return response["solution"]["gRecaptchaResponse"]
except (requests.RequestException, KeyError):
pass

@staticmethod
def check_error(error_code):
if error_code == "ERROR_ZERO_BALANCE":
raise captchaExceptions.NoBalanceException()
elif error_code == "ERROR_WRONG_GOOGLEKEY":
raise captchaExceptions.WrongSitekeyException()
elif error_code == "ERROR_WRONG_USER_KEY" or error_code == "ERROR_KEY_DOES_NOT_EXIST":
raise captchaExceptions.WrongAPIKeyException()
elif error_code == "ERROR_TOO_BIG_CAPTCHA_FILESIZE":
raise captchaExceptions.CaptchaIMGTooBig()
elif error_code == "ERROR_PAGEURL":
raise captchaExceptions.TaskDetails(f"Error: {error_code}")
elif error_code == "MAX_USER_TURN" or error_code == "ERROR_NO_SLOT_AVAILABLE":
raise captchaExceptions.NoSlotAvailable("No slot available")
elif error_code == "ERROR_IP_NOT_ALLOWED" or error_code == "IP_BANNED" or error_code == "ERROR_IP_BLOCKED":
return captchaExceptions.Banned(error_code)
elif error_code == "ERROR_ZERO_CAPTCHA_FILESIZE" or error_code == "ERROR_UPLOAD" or \
error_code == "ERROR_CAPTCHAIMAGE_BLOCKED" or error_code == "ERROR_IMAGE_TYPE_NOT_SUPPORTED" or \
error_code == "ERROR_WRONG_FILE_EXTENSION":
raise captchaExceptions.CaptchaImageError(error_code)
else: raise captchaExceptions.UnknownError(f"Error returned from anticaptcha: {error_code}")
pass
32 changes: 5 additions & 27 deletions captchatools/capsolver.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from . import exceptions as captchaExceptions
from .errors import ErrCodeToException
from . import Harvester
import time
import requests
Expand All @@ -18,7 +18,7 @@ def get_balance(self) -> float:
try:
resp = requests.post(BASEURL + "/getBalance", json=payload ,timeout=20).json()
if resp["errorId"] == 1: # Means there was an error
self.check_error(resp["errorCode"])
ErrCodeToException("Capsolver", resp["errorCode"])
return float(resp["balance"])
except requests.RequestException:
pass
Expand Down Expand Up @@ -103,7 +103,7 @@ def __get_id(self,**kwargs):
try:
resp = requests.post(BASEURL + "/createTask" , json=payload, timeout=20).json()
if resp["errorId"] != 0: # Means there was an error:
self.check_error(resp["errorCode"])
ErrCodeToException("Capsolver", resp["errorCode"])

# Check if there is an answer already available
if resp["status"] == "ready":
Expand All @@ -122,7 +122,7 @@ def __get_answer(self,task_id:int):
try:
response = requests.post(BASEURL + "/getTaskResult",json=payload,timeout=20,).json()
if response["errorId"] != 0: # Means there was an error
self.check_error(response["errorDescription"])
ErrCodeToException("Capsolver", response["errorDescription"])
if response["status"] == "processing":
time.sleep(3)
continue
Expand All @@ -131,26 +131,4 @@ def __get_answer(self,task_id:int):
else:
return response["solution"]["gRecaptchaResponse"]
except (requests.RequestException, KeyError):
pass

@staticmethod
def check_error(error_code):
if error_code == "ERROR_ZERO_BALANCE":
raise captchaExceptions.NoBalanceException()
elif error_code == "ERROR_WRONG_GOOGLEKEY":
raise captchaExceptions.WrongSitekeyException()
elif error_code == "ERROR_WRONG_USER_KEY" or error_code == "ERROR_KEY_DOES_NOT_EXIST":
raise captchaExceptions.WrongAPIKeyException()
elif error_code == "ERROR_TOO_BIG_CAPTCHA_FILESIZE":
raise captchaExceptions.CaptchaIMGTooBig()
elif error_code == "ERROR_REQUIRED_FIELDS":
raise captchaExceptions.TaskDetails(f"Error: {error_code}")
elif error_code == "ERROR_SERVICE_UNAVALIABLE" or error_code == "ERROR_THREADS_MAXIMUM":
raise captchaExceptions.NoSlotAvailable("No slot available")
elif error_code == "ERROR_IP_NOT_ALLOWED" or error_code == "IP_BANNED" or error_code == "ERROR_IP_BLOCKED":
return captchaExceptions.Banned(error_code)
elif error_code == "ERROR_ZERO_CAPTCHA_FILESIZE" or error_code == "ERROR_UPLOAD" or \
error_code == "ERROR_CAPTCHAIMAGE_BLOCKED" or error_code == "ERROR_IMAGE_TYPE_NOT_SUPPORTED" or \
error_code == "ERROR_WRONG_FILE_EXTENSION":
raise captchaExceptions.CaptchaImageError(error_code)
else: raise captchaExceptions.UnknownError(f"Error returned from Capsovler: {error_code}")
pass
96 changes: 96 additions & 0 deletions captchatools/captchaai.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from . import Harvester
import requests
from .errors import ErrCodeToException
from typing import Optional
import time

BASEURL = "https://ocr.captchaai.com/in.php"

class CaptchaAI(Harvester):
def get_balance(self) -> float:
url = f"https://ocr.captchaai.com/res.php?key={self.api_key}&action=getbalance&json=1"
for _ in range(5):
try:
resp = requests.get(url, timeout=20).json()
if resp["status"] == 0: # Means there was an error
ErrCodeToException("CaptchaAI",resp["request"])
return float(resp["request"])
except requests.RequestException:
pass

def get_token(self, b64_img: Optional[str] = None, user_agent: Optional[str] = None, proxy: Optional[str] = None, proxy_type: Optional[str] = None):
# Get ID
task_id = self.__get_id(
b64_img=b64_img,
user_agent=user_agent,
proxy=proxy,
proxy_type=proxy_type
)

# Get Answer
return self.__get_answer(task_id)

def __create_uri_parms(self, **kwargs):
'''Creats the URI params to be used for submitting captcha info'''
params = {
"key": self.api_key,
"json": 1,
"pageurl": self.captcha_url,
}
if self.captcha_type == "image" or self.captcha_type == "normal":
params["method"] = "base64"
params["body"] = kwargs.get("b64_img", "")
elif self.captcha_type == "v2":
params["method"] = "userrecaptcha"
params["googlekey"] = self.sitekey
if self.invisible_captcha:
params["invisible"] = 1
elif self.captcha_type == "v3":
params["method"] = "userrecaptcha"
params["version"] = "v3"
params["googlekey"] = self.sitekey
params["pageurl"] = self.captcha_url
if self.action != "":
params["action"] = self.action
if self.min_score is not None:
params["min_score"] = self.min_score
elif self.captcha_type == "hcap" or self.captcha_type == "hcaptcha":
params["method"] = "hcaptcha"
params["sitekey"] = self.sitekey

# Add Global Data
if kwargs.get("proxy", None) is not None:
params["proxy"] = kwargs.get("proxy")
pxy_type = kwargs.get("proxy_type", "http")
params["proxytype"] = pxy_type
if kwargs.get("user_agent", None) is not None:
params["userAgent"] = kwargs.get("user_agent")
return params

def __get_id(self,**kwargs):
# Create Payload
params = self.__create_uri_parms(**kwargs)

# Get token & return it
for _ in range(50):
try:
resp = requests.get(BASEURL, params=params ,timeout=20).json()
if resp["status"] == 0: # Means there was an error:
ErrCodeToException("CaptchaAI",resp["request"])
return resp["request"]
except (requests.RequestException, KeyError):
pass

def __get_answer(self,task_id:int):
for _ in range(100):
try:
response = requests.get(f"https://ocr.captchaai.com/res.php?key={self.api_key}&action=get&id={task_id}&json=1",timeout=20,).json()
if response["status"] == 0 and response["request"] != "CAPCHA_NOT_READY": # Error checking
ErrCodeToException("CaptchaAI",response["request"])
if response["status"] == 0 and response["request"] == "CAPCHA_NOT_READY":
time.sleep(4)
continue
return response["request"] # Return the captcha token
except (requests.RequestException, KeyError):
pass

25 changes: 25 additions & 0 deletions captchatools/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
from . import exceptions as captchaExceptions

def ErrCodeToException(site: str, error_code:str):
'''
Converts a given error code from the solving site and raises an appropriate exception
'''
if error_code == "ERROR_ZERO_BALANCE":
raise captchaExceptions.NoBalanceException()
elif error_code == "ERROR_WRONG_GOOGLEKEY":
raise captchaExceptions.WrongSitekeyException()
elif error_code == "ERROR_WRONG_USER_KEY" or error_code == "ERROR_KEY_DOES_NOT_EXIST":
raise captchaExceptions.WrongAPIKeyException()
elif error_code == "ERROR_TOO_BIG_CAPTCHA_FILESIZE":
raise captchaExceptions.CaptchaIMGTooBig()
elif error_code == "ERROR_PAGEURL":
raise captchaExceptions.TaskDetails(f"Error: {error_code}")
elif error_code == "MAX_USER_TURN" or error_code == "ERROR_NO_SLOT_AVAILABLE":
raise captchaExceptions.NoSlotAvailable("No slot available")
elif error_code == "ERROR_IP_NOT_ALLOWED" or error_code == "IP_BANNED":
return captchaExceptions.Banned(error_code)
elif error_code == "ERROR_ZERO_CAPTCHA_FILESIZE" or error_code == "ERROR_UPLOAD" or \
error_code == "ERROR_CAPTCHAIMAGE_BLOCKED" or error_code == "ERROR_IMAGE_TYPE_NOT_SUPPORTED" or \
error_code == "ERROR_WRONG_FILE_EXTENSION":
raise captchaExceptions.CaptchaImageError(error_code)
else: raise captchaExceptions.UnknownError(f"Error returned from {site}: {error_code}")
Loading

0 comments on commit 5be7772

Please sign in to comment.