Skip to content

Commit f0b3e76

Browse files
committed
First commit
0 parents  commit f0b3e76

30 files changed

+879
-0
lines changed

.github/workflows/publish.yml

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Publish Python Package 🐍
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
jobs:
8+
deploy:
9+
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v2
14+
- name: Set up Python
15+
uses: actions/setup-python@v2
16+
with:
17+
python-version: "3.7"
18+
19+
- name: Install setup dependencies
20+
run: make setup_dependencies
21+
22+
- name: Install dependencies
23+
run: make build
24+
25+
- name: Build package
26+
run: python -m build
27+
28+
- name: Publish package to PYPI
29+
uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29
30+
with:
31+
user: __token__
32+
password: ${{ secrets.PYPI_TOKEN }}

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
**/__pycache__
2+
**/.vscode

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2022
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
build:
2+
python -m pip install --upgrade pip
3+
pip install build
4+
5+
install: install_test install_pkg
6+
7+
install_test:
8+
pip install .[test]
9+
10+
install_pkg:
11+
python -m pip install --upgrade pip wheel
12+
pip install .
13+
14+
test:
15+
python -m unittest -v tests/test_tasks.py
16+
17+
setup_dependencies:
18+
pip install setuptools_scm wheel

README.md

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
CapSolver.com package for Python
2+
=
3+
![PyPI - Wheel](https://img.shields.io/pypi/wheel/capsolver_python?style=plastic) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/capsolver_python?style=flat) ![GitHub last commit](https://img.shields.io/github/last-commit/alperensert/capsolver_python?style=flat) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/alperensert/capsolver_python?style=flat) ![PyPI - Downloads](https://img.shields.io/pypi/dm/capsolver_python?style=flat) ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/alperensert/capsolver_python?style=flat) ![GitHub Repo stars](https://img.shields.io/github/stars/alperensert/capsolver_python?style=social)
4+
5+
[CapSolver.com](https://capsolver.com) package for Python3
6+
7+
Register now from [here](https://dashboard.capsolver.com/passport/register?inviteCode=kXa8cbNF-b2l).
8+
9+
If you have any problem with usage, [read the documentation](https://github.com/alperensert/capsolver_python/wiki) or [create an issue](https://github.com/alperensert/capsolver_python/issues/new)
10+
11+
### Installation
12+
```
13+
pip install capsolver_python
14+
```
15+
16+
### Supported captcha types
17+
- Image to Text
18+
- Recaptcha V2
19+
- Recaptcha V2 Enterprise
20+
- Recaptcha V3
21+
- FunCaptcha
22+
- HCaptcha
23+
- GeeTest
24+
- AntiAkamaiBMP
25+
- HCaptcha Classification
26+
- DataDome Slider
27+
- FunCaptcha Classification
28+
29+
Usage examples
30+
-
31+
32+
#### ImageToText
33+
34+
```python
35+
from capsolver_python import ImageToTextTask
36+
37+
capsolver = ImageToTextTask("API_KEY")
38+
task_id = capsolver.create_task(image_path="img.png")
39+
result = capsolver.join_task_result(task_id)
40+
print(result.get("text"))
41+
```
42+
43+
#### Recaptcha v2
44+
45+
```python
46+
from capsolver_python import RecaptchaV2Task
47+
48+
capsolver = RecaptchaV2Task("API_KEY")
49+
task_id = capsolver.create_task("website_url", "website_key")
50+
result = capsolver.join_task_result(task_id)
51+
print(result.get("gRecaptchaResponse"))
52+
```
53+
54+
#### Recaptcha v2 enterprise
55+
56+
```python
57+
from capsolver_python import RecaptchaV2EnterpriseTask
58+
59+
capsolver = RecaptchaV2EnterpriseTask("API_KEY")
60+
task_id = capsolver.create_task("website_url", "website_key", {"s": "payload value"}, "api_domain")
61+
result = capsolver.join_task_result(task_id)
62+
print(result.get("gRecaptchaResponse"))
63+
```
64+
65+
#### GeeTest
66+
67+
```python
68+
from capsolver_python import GeeTestTask
69+
70+
capsolver = GeeTestTask("API_KEY")
71+
task_id = capsolver.create_task("website_url", "gt", "challenge")
72+
result= capsolver.join_task_result(task_id)
73+
print(result.get("challenge"))
74+
print(result.get("seccode"))
75+
print(result.get("validate"))
76+
```
77+
78+
For other examples and api documentation please visit [wiki](https://captchaai.atlassian.net/wiki/spaces/CAPTCHAAI/overview)

capsolver_python/__init__.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from .antiakamai_bmp import AntiAkamaiBMPTask
2+
from .antikasada import AntiKasadaTask
3+
from .datadome_slider import DataDomeSliderTask
4+
from .recaptcha_v2 import RecaptchaV2Task
5+
from .recaptcha_v2_enterprise import RecaptchaV2EnterpriseTask
6+
from .fun_captcha import FunCaptchaTask
7+
from .funcaptcha_classification import FunCaptchaClassificationTask
8+
from .hcaptcha_classification import HCaptchaClassificationTask
9+
from .image_to_text import ImageToTextTask
10+
from .gee_test import GeeTestTask
11+
from .h_captcha import HCaptchaTask
12+
from .recaptcha_v3 import RecaptchaV3Task
13+
from .utils import CapSolverException

capsolver_python/antiakamai_bmp.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from .capsolver import CapSolver
2+
from .request_type import RequestType
3+
4+
5+
class AntiAkamaiBMPTask(CapSolver):
6+
'''
7+
Class to solve AntiAkamaiBMP tasks.
8+
9+
Only IOS sensor is supported, and you'll need to implement the tls by yourself
10+
'''
11+
def __init__(self, client_key: str, beta: bool = False) -> None:
12+
super(AntiAkamaiBMPTask, self).__init__(client_key, beta)
13+
14+
def create_task(self, package_name: str, version: str = None, device_id: str = None,
15+
device_name: str = None, count: int = None) -> str:
16+
data = {
17+
"clientKey": self.client_key,
18+
"task": {
19+
"type": "AntiAkamaiBMPTask",
20+
"packageName": package_name
21+
}
22+
}
23+
if version is not None:
24+
data["task"]["version"] = version
25+
if device_id is not None:
26+
data["task"]["deviceId"] = device_id
27+
if device_name is not None:
28+
data["task"]["deviceName"] = device_name
29+
if count is not None:
30+
data["task"]["count"] = count
31+
return self._make_request(RequestType.CreateTaskAntiAkamai, data).get("taskId")

capsolver_python/antikasada.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from .proxy import Proxy
2+
from .useragent import UserAgent
3+
from .request_type import RequestType
4+
5+
6+
class AntiKasadaTask(UserAgent, Proxy):
7+
'''
8+
Class to solve AntiKasada tasks.
9+
10+
This task type AntiKasadaTask require that you send us your proxies.
11+
12+
We support the following proxy types: SOCKS4, SOCKS5, HTTP, HTTPS with authentication by IP address or login and password.
13+
14+
If you want to use proxies by authentication by IP address, please add into the whitelist this ip: 47.253.53.46
15+
'''
16+
def __init__(self, client_key: str, beta: bool = False) -> None:
17+
super(AntiKasadaTask, self).__init__(client_key, beta)
18+
19+
def create_task(self, page_url: str, only_cd: str = None) -> str:
20+
data = {
21+
"clientKey": self.client_key,
22+
"task": {
23+
"pageURL": page_url
24+
}
25+
}
26+
if only_cd is not None:
27+
data["task"]["onlyCD"] = only_cd
28+
return self._make_request(RequestType.CreateTaskKasada, data).get("taskId")

capsolver_python/capsolver.py

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import asyncio
2+
import requests
3+
from time import sleep
4+
from .request_type import RequestType
5+
from .utils import CapSolverException
6+
7+
class CapSolver:
8+
_HOST_URL = "https://api.capsolver.com"
9+
_BETA_HOST_URL = "https://api-beta.capsolver.com"
10+
11+
def __init__(self, client_key: str, beta: bool = False) -> None:
12+
self.client_key = client_key
13+
self.beta = beta
14+
15+
def get_balance(self) -> float:
16+
'''
17+
Returns balance for API key.
18+
'''
19+
data = {"clientKey": self.client_key}
20+
return self._make_request(RequestType.GetBalance, data).get("balance")
21+
22+
def get_packages(self) -> list:
23+
'''
24+
Returns a list of monthly packages.
25+
'''
26+
data = {"clientKey": self.client_key}
27+
return self._make_request(RequestType.GetBalance, data).get("packages")
28+
29+
def get_task_result(self, task_id: str):
30+
data = {
31+
"clientKey": self.client_key,
32+
"taskId": task_id
33+
}
34+
task_result = self._make_request(RequestType.GetTaskResult, data)
35+
return self._is_ready(task_result)
36+
37+
def join_task_result(self, task_id: str, maximum_time: int = 90):
38+
for i in range(0, maximum_time + 1, 1):
39+
result = self.get_task_result(task_id)
40+
if result is not False and result is not None:
41+
return result
42+
elif result is False:
43+
i += 1
44+
sleep(1)
45+
raise CapSolverException(61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed.")
46+
47+
async def join_task_result_async(self, task_id: str, maximum_time: int = 90):
48+
for i in range(0, maximum_time + 1, 1):
49+
result = self.get_task_result(task_id)
50+
if result is not False and result is not None:
51+
return result
52+
elif result is False:
53+
i += 1
54+
await asyncio.sleep(1)
55+
raise CapSolverException(61, "ERROR_MAXIMUM_TIME_EXCEED", "Maximum time is exceed.")
56+
57+
# TODO: Get a soft id for this one
58+
def _make_request(self, method: RequestType, data: dict):
59+
if method == RequestType.CreateTask or method == RequestType.CreateTaskAntiAkamai or method == RequestType.CreateTaskKasada:
60+
data["appId"] = "0C39FC4D-C1FB-4F4E-975B-89E93B78A97A"
61+
try:
62+
response = requests.post("{}{}".format(self._BETA_HOST_URL if self.beta else self._HOST_URL, method), json=data).json()
63+
except Exception as err:
64+
raise CapSolverException(-1, type(err).__name__, str(err))
65+
return response
66+
67+
@staticmethod
68+
def _is_ready(response: dict):
69+
status = response.get("status")
70+
if status == "ready" or status == "processing":
71+
return False if status == "processing" else response.get("solution")
72+
else:
73+
raise CapSolverException(response.get("errorId"), response.get("errorCode"), response.get("errorDescription"))
74+
75+
@staticmethod
76+
def _add_cookies(cookies, data):
77+
if cookies is None:
78+
return data
79+
str_cookies = ""
80+
if type(cookies) == dict:
81+
for key, value in cookies.items():
82+
if value == list(cookies.items())[-1][1]:
83+
str_cookies += "{}={}".format(key, value)
84+
else:
85+
str_cookies += "{}={};".format(key, value)
86+
elif type(cookies) == list:
87+
for i in cookies:
88+
if not len(cookies) % 2 == 0:
89+
raise AttributeError("List cookies length must be even numbers")
90+
if cookies.index(i) % 2 == 0:
91+
str_cookies += "{}=".format(i)
92+
elif cookies[cookies.index(i)] == cookies[-1]:
93+
str_cookies += "{}".format(i)
94+
elif cookies.index(i) % 2 == 1:
95+
str_cookies += "{};".format(i)
96+
elif type(cookies) == str:
97+
data["task"]["cookies"] = cookies
98+
return data
99+
data["task"]["cookies"] = str_cookies
100+
return data

capsolver_python/datadome_slider.py

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
from .proxy import Proxy
2+
from .useragent import UserAgent
3+
from .request_type import RequestType
4+
5+
6+
class DataDomeSliderTask(UserAgent, Proxy):
7+
'''
8+
Class to solve Data dome slider tasks.
9+
10+
This task type DatadomeSliderTask require that you send us your proxies.
11+
12+
We support the following proxy types: SOCKS4, SOCKS5, HTTP, HTTPS with authentication by IP address or login and password.
13+
14+
If you want to use proxies by authentication by IP address, please add into the whitelist this ip: 47.253.53.46
15+
'''
16+
def __init__(self, client_key: str, beta: bool = False) -> None:
17+
super(DataDomeSliderTask, self).__init__(client_key, beta)
18+
19+
def create_task(self, website_url: str, captcha_url: str) -> str:
20+
data = {
21+
"clientKey": self.client_key,
22+
"task": {
23+
"type": "DatadomeSliderTask",
24+
"websiteURL": website_url,
25+
"captchaUrl": captcha_url
26+
}
27+
}
28+
return self._make_request(RequestType.CreateTask, data).get("taskId")

0 commit comments

Comments
 (0)