Skip to content

Commit 6f603d7

Browse files
authored
Merge pull request #39 from ZennoLab/grk717/new-types
add temu, prosopo
2 parents 78d175c + f1db22d commit 6f603d7

10 files changed

Lines changed: 251 additions & 5 deletions

File tree

capmonstercloud_client/CapMonsterCloudClient.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
((ImpervaCustomTaskRequest,), getImpervaTimeouts),
3333
((RecognitionComplexImageTaskRequest), getCITTimeouts),
3434
((MTCaptchaRequest), getImage2TextTimeouts),
35-
((YidunRequest), getImage2TextTimeouts),
35+
((YidunRequest), getYidunTimeouts),
36+
((TemuCustomTaskRequest), getTemuTimeouts),
37+
((ProsopoTaskRequest), getProsopoTimeouts),
3638
)
3739

3840

@@ -83,7 +85,9 @@ async def solve_captcha(self, request: Union[
8385
TurnstileRequest,
8486
RecognitionComplexImageTaskRequest,
8587
MTCaptchaRequest,
86-
YidunRequest],
88+
YidunRequest,
89+
TemuCustomTaskRequest,
90+
ProsopoTaskRequest],
8791
) -> Dict[str, str]:
8892
'''
8993
Non-blocking method for captcha solving.
@@ -116,7 +120,11 @@ async def _solve(self, request: Union[
116120
BinanceTaskRequest,
117121
ImpervaCustomTaskRequest,
118122
TurnstileRequest,
119-
RecognitionComplexImageTaskRequest],
123+
RecognitionComplexImageTaskRequest,
124+
MTCaptchaRequest,
125+
YidunRequest,
126+
TemuCustomTaskRequest,
127+
ProsopoTaskRequest],
120128
timeouts: GetResultTimeouts,
121129
) -> Dict[str, str]:
122130

capmonstercloud_client/GetResultTimeouts.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,12 @@ def getImpervaTimeouts() -> GetResultTimeouts:
5252

5353
def getCITTimeouts() -> GetResultTimeouts:
5454
return GetResultTimeouts(0.35, 0, 0.2, 10)
55+
56+
def getYidunTimeouts() -> GetResultTimeouts:
57+
return GetResultTimeouts(1, 10, 3, 180)
58+
59+
def getProsopoTimeouts() -> GetResultTimeouts:
60+
return GetResultTimeouts(1, 10, 3, 180)
61+
62+
def getTemuTimeouts() -> GetResultTimeouts:
63+
return GetResultTimeouts(1, 10, 3, 180)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import Dict, Union
2+
from pydantic import Field
3+
from .baseRequestWithProxy import BaseRequestWithProxy
4+
5+
6+
class ProsopoTaskRequest(BaseRequestWithProxy):
7+
type: str = Field(default="ProsopoTask")
8+
websiteUrl: str
9+
websiteKey: str
10+
11+
def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
12+
task = {}
13+
task["type"] = self.type
14+
task["websiteURL"] = self.websiteUrl
15+
task["websiteKey"] = self.websiteKey
16+
if self.proxy:
17+
task["proxyType"] = self.proxy.proxyType
18+
task["proxyAddress"] = self.proxy.proxyAddress
19+
task["proxyPort"] = self.proxy.proxyPort
20+
task["proxyLogin"] = self.proxy.proxyLogin
21+
task["proxyPassword"] = self.proxy.proxyPassword
22+
23+
return task
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
from typing import Dict, Union
2+
from pydantic import Field, validator
3+
4+
from .CustomTaskRequestBase import CustomTaskRequestBase
5+
6+
class TemuCustomTaskRequest(CustomTaskRequestBase):
7+
captchaClass: str = Field(default='Temu')
8+
metadata: Dict[str, str]
9+
10+
@validator('metadata')
11+
def validate_metadata(cls, value):
12+
if value.get('cookie') is None:
13+
raise TypeError(f'Expect that cookie will be defined.')
14+
else:
15+
if not isinstance(value.get('cookie'), str):
16+
raise TypeError(f'Expect that cookie will be str.')
17+
if not set(value.keys()).issubset(set(["cookie"])):
18+
raise TypeError(f'Allowed keys for metadata are "cookie"')
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['metadata'] = self.metadata
27+
if self.proxy:
28+
task['proxyType'] = self.proxy.proxyType
29+
task['proxyAddress'] = self.proxy.proxyAddress
30+
task['proxyPort'] = self.proxy.proxyPort
31+
task['proxyLogin'] = self.proxy.proxyLogin
32+
task['proxyPassword'] = self.proxy.proxyPassword
33+
if self.userAgent is not None:
34+
task['userAgent'] = self.userAgent
35+
return task

capmonstercloud_client/requests/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from .RecognitionComplexImageTaskRequest import RecognitionComplexImageTaskRequest
2121
from .MTCaptchaRequest import MTCaptchaRequest
2222
from .YidunRequest import YidunRequest
23+
from .ProsopoTaskRequest import ProsopoTaskRequest
24+
from .TemuCustomTaskRequest import TemuCustomTaskRequest
2325
from .proxy_info import ProxyInfo
2426

2527

@@ -35,4 +37,9 @@
3537
'BinanceTaskRequest',
3638
'ImpervaCustomTaskRequest',
3739
'TurnstileRequest',
38-
'RecognitionComplexImageTaskRequest']
40+
'RecognitionComplexImageTaskRequest',
41+
'MTCaptchaRequest',
42+
'YidunRequest',
43+
'ProsopoTaskRequest',
44+
'TemuCustomTaskRequest'
45+
]

capmonstercloud_client/version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.0
1+
3.3.0

examples/prosopo.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import os
2+
import time
3+
import asyncio
4+
5+
from capmonstercloudclient.requests import ProsopoTaskRequest
6+
from capmonstercloudclient import ClientOptions, CapMonsterClient
7+
8+
9+
async def solve_captcha_sync(num_requests):
10+
return [await cap_monster_client.solve_captcha(prosopo_request) for _ in range(num_requests)]
11+
12+
13+
async def solve_captcha_async(num_requests):
14+
tasks = [asyncio.create_task(cap_monster_client.solve_captcha(prosopo_request)) for _ in range(num_requests)]
15+
return await asyncio.gather(*tasks, return_exceptions=True)
16+
17+
18+
if __name__ == '__main__':
19+
key = os.getenv('API_KEY')
20+
client_options = ClientOptions(api_key=key)
21+
cap_monster_client = CapMonsterClient(options=client_options)
22+
23+
prosopo_request = ProsopoTaskRequest(
24+
websiteUrl='https://example.com/login',
25+
websiteKey='5EZU3LG31uzq1Mwi8inwqxmfvFDpj7VzwDnZwj4Q3syyxBwV'
26+
)
27+
print(prosopo_request.getTaskDict())
28+
nums = 3
29+
30+
# Sync test
31+
sync_start = time.time()
32+
sync_responses = asyncio.run(solve_captcha_sync(nums))
33+
print(f'average execution time sync {1/((time.time()-sync_start)/nums):0.2f} resp/sec\nsolution: {sync_responses[0]}')
34+
35+
# Async test
36+
async_start = time.time()
37+
async_responses = asyncio.run(solve_captcha_async(nums))
38+
print(f'average execution time async {1/((time.time()-async_start)/nums):0.2f} resp/sec\nsolution: {async_responses[0]}')

examples/temu.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import os
2+
import time
3+
import asyncio
4+
5+
from capmonstercloudclient.requests import TemuCustomTaskRequest
6+
from capmonstercloudclient import ClientOptions, CapMonsterClient
7+
8+
9+
async def solve_captcha_sync(num_requests):
10+
return [await cap_monster_client.solve_captcha(temu_request) for _ in range(num_requests)]
11+
12+
13+
async def solve_captcha_async(num_requests):
14+
tasks = [asyncio.create_task(cap_monster_client.solve_captcha(temu_request)) for _ in range(num_requests)]
15+
return await asyncio.gather(*tasks, return_exceptions=True)
16+
17+
18+
if __name__ == '__main__':
19+
key = os.getenv('API_KEY')
20+
client_options = ClientOptions(api_key=key)
21+
cap_monster_client = CapMonsterClient(options=client_options)
22+
23+
metadata = {
24+
"cookie": "region=141; language=en; currency=EUR; api_uid=CnBpI2fwFW2BogBITHVYAg==; timezone=Europe%2FMoscow; _nano_fp=XpmYXqmJnqX8npXblT_T6~7rkpA2LDnz2BPFuT5m; privacy_setting_detail=%7B%22firstPAds%22%3A0%2C%22adj%22%3A0%2C%22fbsAnlys%22%3A0%2C%22fbEvt%22%3A0%2C%22ggAds%22%3A0%2C%22fbAds%22%3A0%2C%22ttAds%22%3A0%2C%22scAds%22%3A0%2C%22ptAds%22%3A0%2C%22bgAds%22%3A0%2C%22tblAds%22%3A0%2C%22obAds%22%3A0%2C%22vgAds%22%3A0%2C%22idAds%22%3A0%2C%22opAds%22%3A0%2C%22stAds%22%3A0%2C%22pmAds%22%3A0%7D; webp=1; _bee=pgoBlKp038lBhEyoQ4yXnuNrw1X5va2U; verifyAuthToken=QkZmx2TJFbSuuRVD_MKJmA0b84fe3df183da8ab"
25+
}
26+
temu_request = TemuCustomTaskRequest(
27+
websiteUrl='https://www.temu.com/bgn_verification.html?verifyCode=7PRQIzDznoFE67ecZYtRTw394f6185143a4af80&from=https%3A%2F%2Fwww.temu.com%2F&refer_page_name=home&refer_page_id=10005_1743074140645_cwb6un82rq&refer_page_sn=10005&_x_sessn_id=xmp1zuyv7y',
28+
metadata=metadata,
29+
userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
30+
)
31+
32+
nums = 3
33+
print(temu_request.getTaskDict())
34+
# Sync test
35+
sync_start = time.time()
36+
sync_responses = asyncio.run(solve_captcha_sync(nums))
37+
print(f'average execution time sync {1/((time.time()-sync_start)/nums):0.2f} resp/sec\nsolution: {sync_responses[0]}')
38+
39+
# Async test
40+
async_start = time.time()
41+
async_responses = asyncio.run(solve_captcha_async(nums))
42+
print(f'average execution time async {1/((time.time()-async_start)/nums):0.2f} resp/sec\nsolution: {async_responses[0]}')

test/prosopo_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import unittest
2+
3+
from pydantic.error_wrappers import ValidationError
4+
from capmonstercloudclient.requests import ProsopoTaskRequest
5+
6+
7+
class ProsopoTaskRequestTest(unittest.TestCase):
8+
websiteUrlExample = "https://example.com"
9+
websiteKeyExample = "prosopo-public-key-123"
10+
11+
def test_prosopo_request_required_fields(self):
12+
required_fields = ["type", "websiteURL", "websiteKey"]
13+
request = ProsopoTaskRequest(
14+
websiteUrl=self.websiteUrlExample,
15+
websiteKey=self.websiteKeyExample
16+
)
17+
task_dictionary = request.getTaskDict()
18+
for f in required_fields:
19+
self.assertTrue(
20+
f in list(task_dictionary.keys()),
21+
msg=f'Required captcha input key "{f}" does not include to request.',
22+
)
23+
self.assertEqual(task_dictionary["type"], "ProsopoTask")
24+
25+
def test_prosopo_missing_fields(self):
26+
base_kwargs = {}
27+
self.assertRaises(ValidationError, ProsopoTaskRequest, **base_kwargs)
28+
base_kwargs.update({"websiteUrl": self.websiteUrlExample})
29+
self.assertRaises(ValidationError, ProsopoTaskRequest, **base_kwargs)
30+
base_kwargs.update({"websiteKey": self.websiteKeyExample})
31+
ProsopoTaskRequest(**base_kwargs)
32+
33+
34+
if __name__ == "__main__":
35+
unittest.main()

test/temu_test.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import unittest
2+
3+
from pydantic.error_wrappers import ValidationError
4+
from capmonstercloudclient.requests import TemuCustomTaskRequest
5+
6+
7+
class TemuCustomTaskRequestTest(unittest.TestCase):
8+
websiteUrlExample = "https://example.com"
9+
userAgentExample = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
10+
cookieExample = "sessionid=abc123; path=/; domain=.example.com"
11+
12+
def test_temu_request_required_fields(self):
13+
required_fields = ["type", "class", "websiteURL", "metadata"]
14+
metadata_required_fields = ["cookie"]
15+
request = TemuCustomTaskRequest(
16+
websiteUrl=self.websiteUrlExample, metadata={"cookie": self.cookieExample}
17+
)
18+
task_dictionary = request.getTaskDict()
19+
for f in required_fields:
20+
self.assertTrue(
21+
f in list(task_dictionary.keys()),
22+
msg=f'Required captcha input key "{f}" does not include to request.',
23+
)
24+
for f in metadata_required_fields:
25+
self.assertTrue(
26+
f in list(task_dictionary["metadata"].keys()),
27+
msg=f'Required captcha input key "{f}" does not include to request.',
28+
)
29+
30+
def test_temu_metadata_validation(self):
31+
base_kwargs = {"websiteUrl": self.websiteUrlExample, "metadata": {}}
32+
self.assertRaises(TypeError, TemuCustomTaskRequest, **base_kwargs)
33+
base_kwargs["metadata"]["cookie"] = self.cookieExample
34+
TemuCustomTaskRequest(**base_kwargs)
35+
# Unsupported keys should raise error
36+
base_kwargs["metadata"]["extra"] = "not-allowed"
37+
self.assertRaises(TypeError, TemuCustomTaskRequest, **base_kwargs)
38+
39+
def test_temu_missing_fields(self):
40+
base_kwargs = {}
41+
self.assertRaises(ValidationError, TemuCustomTaskRequest, **base_kwargs)
42+
base_kwargs.update({"websiteUrl": self.websiteUrlExample})
43+
self.assertRaises(ValidationError, TemuCustomTaskRequest, **base_kwargs)
44+
base_kwargs.update({"metadata": {"cookie": self.cookieExample}})
45+
TemuCustomTaskRequest(**base_kwargs)
46+
47+
48+
if __name__ == "__main__":
49+
unittest.main()

0 commit comments

Comments
 (0)