Skip to content

Commit e36b408

Browse files
authored
Merge pull request #41 from ZennoLab/grk717/CML-2955
Grk717/cml 2955
2 parents 6f603d7 + f02182a commit e36b408

24 files changed

Lines changed: 524 additions & 30 deletions

capmonstercloud_client/CapMonsterCloudClient.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
((RecaptchaV2Request,), getRecaptchaV2Timeouts),
1818
((RecaptchaV2EnterpriseRequest,), getRecaptchaV2EnterpriseTimeouts),
1919
((RecaptchaV3ProxylessRequest), getRecaptchaV3Timeouts),
20+
((RecaptchaV3EnterpriseRequest), getRecaptchaV3Timeouts),
2021
((ImageToTextRequest), getImage2TextTimeouts),
2122
((FuncaptchaRequest,), getFuncaptchaTimeouts),
2223
((HcaptchaRequest,), getHcaptchaTimeouts),
@@ -35,6 +36,7 @@
3536
((YidunRequest), getYidunTimeouts),
3637
((TemuCustomTaskRequest), getTemuTimeouts),
3738
((ProsopoTaskRequest), getProsopoTimeouts),
39+
((AltchaCustomTaskRequest), getAltchaTimeouts),
3840
)
3941

4042

@@ -44,6 +46,14 @@ def __init__(self,
4446
self.options = options
4547
self._headers = {'User-Agent':
4648
f'Zennolab.CapMonsterCloud.Client.Python/{parseVersion()}'}
49+
if self.options.client_proxy:
50+
if self.options.client_proxy.proxyType not in ['http', 'https']:
51+
raise UnsupportedProxyTypeError(f'Supported client proxy types are: [HTTP, HTTPS]')
52+
if self.options.client_proxy.proxyLogin and self.options.client_proxy.proxyPassword:
53+
auth = f"{self.options.client_proxy.proxyLogin}:{self.options.client_proxy.proxyPassword}@"
54+
self.client_proxy_string = f"{self.options.client_proxy.proxyType}://{auth}{self.options.client_proxy.proxyAddress}:{self.options.client_proxy.proxyPort}"
55+
else:
56+
self.client_proxy_string = None
4757

4858
@property
4959
def headers(self):
@@ -56,7 +66,8 @@ async def get_balance(self) -> Dict[str, Union[int, float, str]]:
5666
async with aiohttp.ClientSession() as session:
5767
async with session.post(url=self.options.service_url + '/getBalance',
5868
json=body,
59-
timeout=aiohttp.ClientTimeout(total=self.options.client_timeout)) as resp:
69+
timeout=aiohttp.ClientTimeout(total=self.options.client_timeout),
70+
proxy=self.client_proxy_string) as resp:
6071
if resp.status != 200:
6172
raise HTTPException(f'Cannot create task. Status code: {resp.status}.')
6273
result = await resp.json(content_type=None)
@@ -69,6 +80,7 @@ async def solve_captcha(self, request: Union[
6980
RecaptchaV2EnterpriseRequest,
7081
RecaptchaV2Request,
7182
RecaptchaV3ProxylessRequest,
83+
RecaptchaV3EnterpriseRequest,
7284
RecaptchaComplexImageTaskRequest,
7385
ImageToTextRequest,
7486
FuncaptchaRequest,
@@ -87,7 +99,8 @@ async def solve_captcha(self, request: Union[
8799
MTCaptchaRequest,
88100
YidunRequest,
89101
TemuCustomTaskRequest,
90-
ProsopoTaskRequest],
102+
ProsopoTaskRequest,
103+
AltchaCustomTaskRequest],
91104
) -> Dict[str, str]:
92105
'''
93106
Non-blocking method for captcha solving.
@@ -106,6 +119,7 @@ async def _solve(self, request: Union[
106119
RecaptchaV2EnterpriseRequest,
107120
RecaptchaV2Request,
108121
RecaptchaV3ProxylessRequest,
122+
RecaptchaV3EnterpriseRequest,
109123
RecaptchaComplexImageTaskRequest,
110124
ImageToTextRequest,
111125
FuncaptchaRequest,
@@ -124,7 +138,8 @@ async def _solve(self, request: Union[
124138
MTCaptchaRequest,
125139
YidunRequest,
126140
TemuCustomTaskRequest,
127-
ProsopoTaskRequest],
141+
ProsopoTaskRequest,
142+
AltchaCustomTaskRequest],
128143
timeouts: GetResultTimeouts,
129144
) -> Dict[str, str]:
130145

@@ -168,7 +183,8 @@ async def _getTaskResult(self, task_id: str) -> Dict[str, Union[int, str, None]]
168183
async with session.post(url=self.options.service_url + '/getTaskResult',
169184
json=body,
170185
timeout=aiohttp.ClientTimeout(total=self.options.client_timeout),
171-
headers=self.headers) as resp:
186+
headers=self.headers,
187+
proxy=self.client_proxy_string) as resp:
172188
if resp.status != 200:
173189
if resp.status == 500:
174190
return {'errorId': 0, 'status': 'processing'}
@@ -184,11 +200,13 @@ async def _createTask(self, request: BaseRequest) -> Dict[str, Union[str, int]]:
184200
"task": task,
185201
"softId": self.options.default_soft_id
186202
}
203+
187204
async with aiohttp.ClientSession() as session:
188205
async with session.post(url=self.options.service_url + '/createTask',
189206
json=body,
190207
timeout=aiohttp.ClientTimeout(total=self.options.client_timeout),
191-
headers=self.headers) as resp:
208+
headers=self.headers,
209+
proxy=self.client_proxy_string) as resp:
192210
if resp.status != 200:
193211
raise HTTPException(f'Cannot create task. Status code: {resp.status}.')
194212
else:

capmonstercloud_client/GetResultTimeouts.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ def getRecaptchaV2EnterpriseTimeouts() -> GetResultTimeouts:
1717
def getRecaptchaV3Timeouts() -> GetResultTimeouts:
1818
return GetResultTimeouts(1, 10, 3, 180)
1919

20+
def getRecaptchaV3EnterpriseTimeouts() -> GetResultTimeouts:
21+
return GetResultTimeouts(1, 10, 3, 150)
22+
2023
def getImage2TextTimeouts() -> GetResultTimeouts:
2124
return GetResultTimeouts(0.35, 0, 0.2, 10)
2225

@@ -50,6 +53,9 @@ def getBinanceTimeouts() -> GetResultTimeouts:
5053
def getImpervaTimeouts() -> GetResultTimeouts:
5154
return GetResultTimeouts(1, 0, 1, 20)
5255

56+
def getAltchaTimeouts() -> GetResultTimeouts:
57+
return GetResultTimeouts(1, 0, 1, 50)
58+
5359
def getCITTimeouts() -> GetResultTimeouts:
5460
return GetResultTimeouts(0.35, 0, 0.2, 10)
5561

capmonstercloud_client/clientOptions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
from pydantic import BaseModel, validator, Field
2-
2+
from .requests import ClientProxyInfo
3+
from typing import Optional
34

45
class ClientOptions(BaseModel):
56
api_key: str
7+
client_proxy: Optional[ClientProxyInfo] = None
68
service_url: str = Field(default="https://api.capmonster.cloud")
79
default_soft_id: int = Field(default=55)
810
client_timeout: float = Field(default=20.0)
11+
912

1013
@validator('api_key')
1114
def validate_api_key(cls, value):

capmonstercloud_client/exceptions.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class TaskNotDefinedError(BaseError):
2424
class ExtraParamsError(BaseError):
2525
pass
2626

27+
class UnsupportedProxyTypeError(BaseError):
28+
pass
2729

2830

2931
class UserAgentNotDefinedError(BaseError):
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from typing import Dict, Union
2+
from pydantic import Field, validator
3+
4+
from .CustomTaskRequestBase import CustomTaskRequestBase
5+
6+
class AltchaCustomTaskRequest(CustomTaskRequestBase):
7+
captchaClass: str = Field(default='altcha')
8+
websiteKey: str = Field()
9+
metadata : Dict[str, str]
10+
11+
@validator('metadata')
12+
def validate_metadata(cls, value):
13+
for key in ['challenge', 'iterations', 'salt', 'signature']:
14+
if value.get(key) is None:
15+
raise TypeError(f'Expect that {key} will be defined.')
16+
else:
17+
if not isinstance(value.get(key), str):
18+
raise TypeError(f'Expect that {key} will be str.')
19+
return value
20+
21+
def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
22+
task = {}
23+
task['type'] = self.type
24+
task['class'] = self.captchaClass
25+
task['websiteURL'] = self.websiteUrl
26+
task['websiteKey'] = self.websiteKey
27+
task['metadata'] = self.metadata
28+
if self.proxy:
29+
task['proxyType'] = self.proxy.proxyType
30+
task['proxyAddress'] = self.proxy.proxyAddress
31+
task['proxyPort'] = self.proxy.proxyPort
32+
task['proxyLogin'] = self.proxy.proxyLogin
33+
task['proxyPassword'] = self.proxy.proxyPassword
34+
if self.userAgent is not None:
35+
task['userAgent'] = self.userAgent
36+
return task

capmonstercloud_client/requests/CustomTaskRequestBase.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@ class CustomTaskRequestBase(BaseRequestWithProxy):
66
captchaClass: str # Class(subtype) of ComplexImageTask
77
type: str = "CustomTask" # Recognition task type
88
websiteUrl: str # Address of a webpage with captcha
9-
userAgent: Optional[str] = None # It is required that you use a signature of a modern browser
10-
domains: Optional[List[str]] = None # Collection with base64 encoded images. Must be populated if <see cref="ImageUrls"/> not.
9+
userAgent: Optional[str] = None
10+
domains: Optional[List[str]] = None

capmonstercloud_client/requests/DataDomeCustomTaskRequest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ def validate_metadata(cls, value):
1818
return {i: value[i] for i in value if i != 'captchaUrl'}
1919
else:
2020
raise TypeError(f'Expected one of [captchaUrl, htmlPageBase64]')
21+
if value.get('datadomeVersion') and not isinstance(value.get('datadomeVersion'), str):
22+
raise TypeError(f'Expected datadomeVersion to be str')
23+
2124

2225
def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
2326
task = {}

capmonstercloud_client/requests/FuncaptchaRequest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class FuncaptchaRequest(BaseRequestWithProxy):
99
websitePublicKey: str
1010
funcaptchaApiJSSubdomain: Optional[str] = Field(default=None)
1111
data: Optional[str] = Field(default=None)
12+
cookies: Optional[str] = Field(default=None)
1213

1314
def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
1415
task = {}
@@ -26,4 +27,6 @@ def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
2627
task['funcaptchaApiJSSubdomain'] = self.funcaptchaApiJSSubdomain
2728
if self.data is not None:
2829
task['data'] = self.data
30+
if self.cookies is not None:
31+
task['cookies'] = self.cookies
2932
return task

capmonstercloud_client/requests/ImpervaCustomTaskRequest.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Dict, Union
2-
from pydantic import Field, validator
2+
from pydantic import Field, validator, model_validator
33

44
from .CustomTaskRequestBase import CustomTaskRequestBase
55

@@ -23,18 +23,24 @@ def validate_metadata(cls, value):
2323
raise TypeError(f'Expect that reese84UrlEndpoint will be str.')
2424
return value
2525

26+
@model_validator(mode='before')
27+
def validate_imperva_proxy(cls, values):
28+
proxy = values.get('proxy')
29+
if proxy is None:
30+
raise RuntimeError(f'You are required to use your own proxies.')
31+
return values
32+
2633
def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
2734
task = {}
2835
task['type'] = self.type
2936
task['class'] = self.captchaClass
3037
task['websiteURL'] = self.websiteUrl
3138
task['metadata'] = self.metadata
32-
if self.proxy:
33-
task['proxyType'] = self.proxy.proxyType
34-
task['proxyAddress'] = self.proxy.proxyAddress
35-
task['proxyPort'] = self.proxy.proxyPort
36-
task['proxyLogin'] = self.proxy.proxyLogin
37-
task['proxyPassword'] = self.proxy.proxyPassword
39+
task['proxyType'] = self.proxy.proxyType
40+
task['proxyAddress'] = self.proxy.proxyAddress
41+
task['proxyPort'] = self.proxy.proxyPort
42+
task['proxyLogin'] = self.proxy.proxyLogin
43+
task['proxyPassword'] = self.proxy.proxyPassword
3844
if self.userAgent is not None:
3945
task['userAgent'] = self.userAgent
4046
return task

capmonstercloud_client/requests/RecaptchaV2EnterpiseRequest.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ class RecaptchaV2EnterpriseRequest(BaseRequestWithProxy):
99
websiteKey: str
1010
enterprisePayload: Optional[str] = Field(default=None)
1111
apiDomain: Optional[str] = Field(default=None)
12+
pageAction: Optional[str] = Field(default=None)
1213

1314
def getTaskDict(self) -> Dict[str, Union[str, int]]:
1415
task = {}
@@ -25,4 +26,6 @@ def getTaskDict(self) -> Dict[str, Union[str, int]]:
2526
task['enterprisePayload'] = {'s': self.enterprisePayload}
2627
if self.apiDomain is not None:
2728
task['apiDomain'] = self.apiDomain
29+
if self.pageAction is not None:
30+
task['pageAction'] = self.pageAction
2831
return task

0 commit comments

Comments
 (0)