Skip to content

Commit

Permalink
1.2.1
Browse files Browse the repository at this point in the history
Python:
[+] Added soft_id support for 2captcha
[+] Added text-image captcha support
[+] Updated documentation
[-] Removed redundant unnecessary exception handling code
  • Loading branch information
Matthew17-21 committed Nov 21, 2021
1 parent e06b698 commit 8441e06
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 47 deletions.
16 changes: 13 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ pip3 install captchatools
```python
pip3 install -U captchatools
```

# How to use
### Getting reCAPTCHA Tokens
```python
import captchatools
solver = captchatools.captcha_harvesters(solving_site="capmonster", api_key="YOUR API KEY", sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-", captcha_url="https://www.google.com/recaptcha/api2/demo")
Expand All @@ -26,6 +26,14 @@ solver = captcha_harvesters(solving_site=1, api_key="YOUR API KEY", sitekey="6Le
captcha_answer = solver.get_token()
```

### Getting Normal Captcha Tokens
```python
import captchatools
solver = captchatools.captcha_harvesters(solving_site=2, captcha_type="normal", api_key="YOUR API KEY HERE")
url = "https://www.scienceabc.com/wp-content/uploads/ext-www.scienceabc.com/wp-content/uploads/2016/07/Captcha-ex.jpg-.jpg"
text_cap_answer = solver.get_normal(url)
```

| Parameter | Required | Type | Default | Description|
| :-------------: |:-------------:| :-----:| :-----:| :-----:|
| api_key | true | String| -| The API Key for the captcha solving site|
Expand All @@ -46,8 +54,8 @@ captcha_answer = solver.get_token()
##### Site-Specific Support:
| Site |Site ID| Captcha Types Supported | Task Types Supported|
| :-------------: |:-------------:|:-------------:| :-----:|
| Capmonster |1| Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | RecaptchaV2TaskProxyless,<br />RecaptchaV3TaskProxyless,<br />HCaptchaTaskProxyless |
| Anticaptcha |2| Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | RecaptchaV2TaskProxyless,<br />RecaptchaV3TaskProxyless,<br />HCaptchaTaskProxyless |
| Capmonster |1| Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | RecaptchaV2TaskProxyless,<br />RecaptchaV3TaskProxyless,<br />HCaptchaTaskProxyless, <br />ImageToTextTask<br />|
| Anticaptcha |2| Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | RecaptchaV2TaskProxyless,<br />RecaptchaV3TaskProxyless,<br />HCaptchaTaskProxyless <br />ImageToTextTask<br />|
| 2Captcha |3| Recaptcha V2,<br />Recaptcha V3,<br />HCaptcha | - |


Expand All @@ -63,6 +71,8 @@ captcha_answer = solver.get_token()
| `WrongAPIKeyExceptionException` | Incorrect API Key for captcha solving site|
| `WrongSitekeyException` | Incorrect sitekey |
| `NoHarvesterException` | When the user did not / incorrectly chose a captcha harvester. Refer to the [guide](https://github.com/Matthew17-21/Captcha-Tools#how-to-use) |
| `CaptchaIMGTooBig` | The size of the captcha image is too big for the solving service. |
| `FailedToGetCapIMG`| Failed to get the captcha image from the URL. <br />**Tries 3 times before getting thrown.**<br />

```python
from captchatools import captcha_harvesters, exceptions as captchaExceptions
Expand Down
2 changes: 0 additions & 2 deletions captchatools-go/anticaptcha.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package captchatoolsgo
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
Expand Down Expand Up @@ -72,7 +71,6 @@ func (t *Anticaptcha) getCaptchaAnswer() (string, error) {
// Parse Response
body, _ := ioutil.ReadAll(resp.Body)
resp.Body.Close()
fmt.Println(string(body))
json.Unmarshal(body, response)
if response.Status == "ready" {
return response.Solution.GRecaptchaResponse, nil
Expand Down
26 changes: 16 additions & 10 deletions captchatools/anticaptcha.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ def __init__(self, parent:object):

def get_token(self) -> str:
return self.get_answer( self.get_id() )

def get_normal(self, cap_pic_url) -> str:
return self.get_answer(self.get_id(cap_pic_url), True)

def get_id(self) -> int:
def get_id(self, cap_pic_url=None) -> int:
'''
Method to get Queue ID from the API.
'''
Expand All @@ -44,6 +47,10 @@ def get_id(self) -> int:

elif self.user_data.captcha_type == "hcap" or self.user_data.captcha_type == "hcaptcha":
payload["task"]["type"] = "HCaptchaTaskProxyless"

elif self.user_data.captcha_type == "normal":
payload["task"]["type"] = "ImageToTextTask"
payload["task"]["body"] = self.user_data.get_cap_img(cap_pic_url)

# Get the Queue ID b sending a POST request to their API
while True:
Expand All @@ -61,16 +68,13 @@ def get_id(self) -> int:
elif resp["errorCode"] == "ERROR_KEY_DOES_NOT_EXIST":
# Throw Exception
raise captchaExceptions.WrongAPIKeyException()
except captchaExceptions.NoBalanceException:
raise captchaExceptions.NoBalanceException()
except captchaExceptions.WrongSitekeyException:
raise captchaExceptions.WrongSitekeyException()
except captchaExceptions.WrongAPIKeyException:
raise captchaExceptions.WrongAPIKeyException()
except Exception:

elif resp["errorCode"] == "ERROR_TOO_BIG_CAPTCHA_FILESIZE":
raise captchaExceptions.CaptchaIMGTooBig()
except requests.RequestException:
pass

def get_answer(self, queue_id) -> str:
def get_answer(self, queue_id, isTextCap=False) -> str:
'''
This method gets the captcha token from the API
'''
Expand All @@ -84,8 +88,10 @@ def get_answer(self, queue_id) -> str:
}

answer = requests.post(BASEURL + "/getTaskResult", json=payload_getAnswer).json()
if answer["status"] == "ready":
if answer["status"] == "ready" and not isTextCap:
return answer["solution"]["gRecaptchaResponse"]
elif answer["status"] == "ready" and isTextCap:
return answer["solution"]["text"]

elif answer["errorId"] == 12 or answer["errorId"] == 16:
# Captcha unsolvable || TaskID doesn't exist
Expand Down
37 changes: 21 additions & 16 deletions captchatools/capmonster.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,11 @@ def __init__(self, parent:object):

def get_token(self) -> str:
return self.get_answer( self.get_id() )

def get_normal(self, path_to_img) -> str:
return self.get_answer(self.get_id(path_to_img), True)

def get_id(self) -> int:
def get_id(self, cap_pic_url=None) -> int:
'''
Method to get Queue ID from the API.
'''
Expand All @@ -43,6 +46,11 @@ def get_id(self) -> int:
payload["task"]["pageAction"] = self.user_data.action
elif self.user_data.captcha_type == "hcap" or self.user_data.captcha_type == "hcaptcha":
payload["task"]["type"] = "HCaptchaTaskProxyless"

elif self.user_data.captcha_type == "normal":
payload["task"]["type"] = "ImageToTextTask"
payload["task"]["body"] = self.user_data.get_cap_img(cap_pic_url)


# Get the Queue ID b sending a POST request to their API
while True:
Expand All @@ -62,30 +70,27 @@ def get_id(self) -> int:
elif resp["errorCode"] == "ERROR_KEY_DOES_NOT_EXIST":
# Throw Exception
raise captchaExceptions.WrongAPIKeyException()

except captchaExceptions.NoBalanceException:
raise captchaExceptions.NoBalanceException()
except captchaExceptions.WrongSitekeyException:
raise captchaExceptions.WrongSitekeyException()
except captchaExceptions.WrongAPIKeyException:
raise captchaExceptions.WrongAPIKeyException()
except Exception:

elif resp["errorCode"] == "ERROR_TOO_BIG_CAPTCHA_FILESIZE":
raise captchaExceptions.CaptchaIMGTooBig()
except requests.RequestException:
pass

def get_answer(self, queue_id) -> str:
def get_answer(self, queue_id, isTextCap=False) -> str:
'''
This method gets the captcha token from the API
'''
payload_getAnswer = {
"clientKey":self.user_data.api_key,
"taskId": queue_id
}
while True:
try:
payload_getAnswer = {
"clientKey":self.user_data.api_key,
"taskId": queue_id
}

answer = requests.post(BASEURL + "/getTaskResult", json=payload_getAnswer).json()
if answer["status"] == "ready":
if answer["status"] == "ready" and not isTextCap:
return answer["solution"]["gRecaptchaResponse"]
elif answer["status"] == "ready" and isTextCap:
return answer["solution"]["text"]

elif answer["errorId"] == 12 or answer["errorId"] == 16:
# Captcha unsolvable || TaskID doesn't exist
Expand Down
18 changes: 16 additions & 2 deletions captchatools/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

import requests.exceptions
class WrongAPIKeyException(Exception):
'''
This exception gets thrown when the user provides a wrong API Key
Expand Down Expand Up @@ -27,4 +27,18 @@ class NoHarvesterException(Exception):
This exception gets thrown when a user doesn't properly set a harvester.
'''
def __init__(self, message="[captchatools] No captcha harvester selected"):
super(NoHarvesterException, self).__init__(message)
super(NoHarvesterException, self).__init__(message)

class CaptchaIMGTooBig(Exception):
'''
This exception gets thrown when the filesize of the captcha image is too big for the solving site.
'''
def __init__(self, message="[captchatools] Size of the captcha image is too big."):
super(CaptchaIMGTooBig, self).__init__(message)

class FailedToGetCapIMG(Exception):
'''
This exception gets thrown when the program fails to get the captcha image after 3 tries
'''
def __init__(self, message="[captchatools] Failed to fetch captcha image."):
super(FailedToGetCapIMG, self).__init__(message)
38 changes: 35 additions & 3 deletions captchatools/harvesters.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from .twocap import Twocap
from .anticaptcha import Anticap
from .capmonster import Capmonster
from .exceptions import NoHarvesterException

from .exceptions import NoHarvesterException, FailedToGetCapIMG
import requests
import base64
class captcha_harvesters:
def __init__( self, solving_site=1,
api_key="Key the site gave you to solve captchas",
Expand Down Expand Up @@ -40,6 +41,37 @@ def __init__( self, solving_site=1,

def get_token(self):
'''
Returns a captcha token for the provided details
Returns a recaptcha token for the provided details
'''
return self.harvester.get_token()

def get_normal(self, path_to_image):
'''
This method will handle returning text from 'Normal Captchas.'
As per 2captcha,
"Normal Captcha is an image that contains distored but human-readable text. To solve the captcha user have to type the text from the image."
Parameters:
- path_to_image
- The path where the captcha is located (you must download the image yourself and then pass it in)
'''
return self.harvester.get_normal(path_to_image)

@staticmethod
def get_cap_img(img_url:str) -> str:
'''
This function tries 3 times to get the captcha image.
If successful, it returns the base64 encoded image.
If not successful, raises a FailedToGetCapIMG exception
'''
for _ in range(3):
try:
response = requests.get(img_url)
b64response = base64.b64encode(response.content).decode("utf-8")
return b64response
except:
pass
raise FailedToGetCapIMG()
24 changes: 13 additions & 11 deletions captchatools/twocap.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ def __init__(self, parent:object):
def get_token(self) -> str:
return self.get_answer( self.get_id() )

def get_id(self) -> int:
def get_normal(self, cap_pic_url) -> str:
return self.get_answer( self.get_id(cap_pic_url) )

def get_id(self, cap_pic_url=None) -> int:
payload = {
"key": self.user_data.api_key,
"method": "userrecaptcha", # Because V2 recaptcha is defaulted on init, I'll leave this
Expand All @@ -42,9 +45,12 @@ def get_id(self) -> int:
payload.pop("googlekey")
payload["sitekey"] = self.user_data.sitekey

elif self.user_data.captcha_type == "normal":
payload["method"] = "base64"
payload["body"] = self.user_data.get_cap_img(cap_pic_url)
while True:
try:
resp = requests.post(BASEURL, json=payload).json()
resp = requests.post(BASEURL, data=payload).json()
if resp["status"] == 1:
return resp["request"] # Return the queue ID

Expand All @@ -59,16 +65,12 @@ def get_id(self) -> int:
elif resp["request"] == "ERROR_WRONG_USER_KEY" or resp["request"] == "ERROR_KEY_DOES_NOT_EXIST":
# Throw Exception
raise captchaExceptions.WrongAPIKeyException()

elif resp["request"] == "ERROR_TOO_BIG_CAPTCHA_FILESIZE":
raise captchaExceptions.CaptchaIMGTooBig()
break

except captchaExceptions.NoBalanceException:
raise captchaExceptions.NoBalanceException()
except captchaExceptions.WrongSitekeyException:
raise captchaExceptions.WrongSitekeyException()
except captchaExceptions.WrongAPIKeyException:
raise captchaExceptions.WrongAPIKeyException()

except Exception:
except requests.RequestException:
pass

def get_answer(self, queue_id) -> str:
Expand All @@ -88,4 +90,4 @@ def get_answer(self, queue_id) -> str:
except KeyError:
self.get_token()
except Exception:
pass
pass

0 comments on commit 8441e06

Please sign in to comment.