Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions capmonstercloud_client/CapMonsterCloudClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
((ImpervaCustomTaskRequest,), getImpervaTimeouts),
((RecognitionComplexImageTaskRequest), getCITTimeouts),
((MTCaptchaRequest), getImage2TextTimeouts),
((YidunRequest), getImage2TextTimeouts),
((YidunRequest), getYidunTimeouts),
((TemuCustomTaskRequest), getTemuTimeouts),
((ProsopoTaskRequest), getProsopoTimeouts),
)


Expand Down Expand Up @@ -83,7 +85,9 @@ async def solve_captcha(self, request: Union[
TurnstileRequest,
RecognitionComplexImageTaskRequest,
MTCaptchaRequest,
YidunRequest],
YidunRequest,
TemuCustomTaskRequest,
ProsopoTaskRequest],
) -> Dict[str, str]:
'''
Non-blocking method for captcha solving.
Expand Down Expand Up @@ -116,7 +120,11 @@ async def _solve(self, request: Union[
BinanceTaskRequest,
ImpervaCustomTaskRequest,
TurnstileRequest,
RecognitionComplexImageTaskRequest],
RecognitionComplexImageTaskRequest,
MTCaptchaRequest,
YidunRequest,
TemuCustomTaskRequest,
ProsopoTaskRequest],
timeouts: GetResultTimeouts,
) -> Dict[str, str]:

Expand Down
9 changes: 9 additions & 0 deletions capmonstercloud_client/GetResultTimeouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,12 @@ def getImpervaTimeouts() -> GetResultTimeouts:

def getCITTimeouts() -> GetResultTimeouts:
return GetResultTimeouts(0.35, 0, 0.2, 10)

def getYidunTimeouts() -> GetResultTimeouts:
return GetResultTimeouts(1, 10, 3, 180)

def getProsopoTimeouts() -> GetResultTimeouts:
return GetResultTimeouts(1, 10, 3, 180)

def getTemuTimeouts() -> GetResultTimeouts:
return GetResultTimeouts(1, 10, 3, 180)
23 changes: 23 additions & 0 deletions capmonstercloud_client/requests/ProsopoTaskRequest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from typing import Dict, Union
from pydantic import Field
from .baseRequestWithProxy import BaseRequestWithProxy


class ProsopoTaskRequest(BaseRequestWithProxy):
type: str = Field(default="ProsopoTask")
websiteUrl: str
websiteKey: str

def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
task = {}
task["type"] = self.type
task["websiteURL"] = self.websiteUrl
task["websiteKey"] = self.websiteKey
if self.proxy:
task["proxyType"] = self.proxy.proxyType
task["proxyAddress"] = self.proxy.proxyAddress
task["proxyPort"] = self.proxy.proxyPort
task["proxyLogin"] = self.proxy.proxyLogin
task["proxyPassword"] = self.proxy.proxyPassword

return task
35 changes: 35 additions & 0 deletions capmonstercloud_client/requests/TemuCustomTaskRequest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from typing import Dict, Union
from pydantic import Field, validator

from .CustomTaskRequestBase import CustomTaskRequestBase

class TemuCustomTaskRequest(CustomTaskRequestBase):
captchaClass: str = Field(default='Temu')
metadata: Dict[str, str]

@validator('metadata')
def validate_metadata(cls, value):
if value.get('cookie') is None:
raise TypeError(f'Expect that cookie will be defined.')
else:
if not isinstance(value.get('cookie'), str):
raise TypeError(f'Expect that cookie will be str.')
if not set(value.keys()).issubset(set(["cookie"])):
raise TypeError(f'Allowed keys for metadata are "cookie"')
return value

def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
task = {}
task['type'] = self.type
task['class'] = self.captchaClass
task['websiteURL'] = self.websiteUrl
task['metadata'] = self.metadata
if self.proxy:
task['proxyType'] = self.proxy.proxyType
task['proxyAddress'] = self.proxy.proxyAddress
task['proxyPort'] = self.proxy.proxyPort
task['proxyLogin'] = self.proxy.proxyLogin
task['proxyPassword'] = self.proxy.proxyPassword
if self.userAgent is not None:
task['userAgent'] = self.userAgent
return task
9 changes: 8 additions & 1 deletion capmonstercloud_client/requests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from .RecognitionComplexImageTaskRequest import RecognitionComplexImageTaskRequest
from .MTCaptchaRequest import MTCaptchaRequest
from .YidunRequest import YidunRequest
from .ProsopoTaskRequest import ProsopoTaskRequest
from .TemuCustomTaskRequest import TemuCustomTaskRequest
from .proxy_info import ProxyInfo


Expand All @@ -35,4 +37,9 @@
'BinanceTaskRequest',
'ImpervaCustomTaskRequest',
'TurnstileRequest',
'RecognitionComplexImageTaskRequest']
'RecognitionComplexImageTaskRequest',
'MTCaptchaRequest',
'YidunRequest',
'ProsopoTaskRequest',
'TemuCustomTaskRequest'
]
2 changes: 1 addition & 1 deletion capmonstercloud_client/version.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.2.0
3.3.0
38 changes: 38 additions & 0 deletions examples/prosopo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os
import time
import asyncio

from capmonstercloudclient.requests import ProsopoTaskRequest
from capmonstercloudclient import ClientOptions, CapMonsterClient


async def solve_captcha_sync(num_requests):
return [await cap_monster_client.solve_captcha(prosopo_request) for _ in range(num_requests)]


async def solve_captcha_async(num_requests):
tasks = [asyncio.create_task(cap_monster_client.solve_captcha(prosopo_request)) for _ in range(num_requests)]
return await asyncio.gather(*tasks, return_exceptions=True)


if __name__ == '__main__':
key = os.getenv('API_KEY')
client_options = ClientOptions(api_key=key)
cap_monster_client = CapMonsterClient(options=client_options)

prosopo_request = ProsopoTaskRequest(
websiteUrl='https://example.com/login',
websiteKey='5EZU3LG31uzq1Mwi8inwqxmfvFDpj7VzwDnZwj4Q3syyxBwV'
)
print(prosopo_request.getTaskDict())
nums = 3

# Sync test
sync_start = time.time()
sync_responses = asyncio.run(solve_captcha_sync(nums))
print(f'average execution time sync {1/((time.time()-sync_start)/nums):0.2f} resp/sec\nsolution: {sync_responses[0]}')

# Async test
async_start = time.time()
async_responses = asyncio.run(solve_captcha_async(nums))
print(f'average execution time async {1/((time.time()-async_start)/nums):0.2f} resp/sec\nsolution: {async_responses[0]}')
42 changes: 42 additions & 0 deletions examples/temu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
import time
import asyncio

from capmonstercloudclient.requests import TemuCustomTaskRequest
from capmonstercloudclient import ClientOptions, CapMonsterClient


async def solve_captcha_sync(num_requests):
return [await cap_monster_client.solve_captcha(temu_request) for _ in range(num_requests)]


async def solve_captcha_async(num_requests):
tasks = [asyncio.create_task(cap_monster_client.solve_captcha(temu_request)) for _ in range(num_requests)]
return await asyncio.gather(*tasks, return_exceptions=True)


if __name__ == '__main__':
key = os.getenv('API_KEY')
client_options = ClientOptions(api_key=key)
cap_monster_client = CapMonsterClient(options=client_options)

metadata = {
"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"
}
temu_request = TemuCustomTaskRequest(
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',
metadata=metadata,
userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36',
)

nums = 3
print(temu_request.getTaskDict())
# Sync test
sync_start = time.time()
sync_responses = asyncio.run(solve_captcha_sync(nums))
print(f'average execution time sync {1/((time.time()-sync_start)/nums):0.2f} resp/sec\nsolution: {sync_responses[0]}')

# Async test
async_start = time.time()
async_responses = asyncio.run(solve_captcha_async(nums))
print(f'average execution time async {1/((time.time()-async_start)/nums):0.2f} resp/sec\nsolution: {async_responses[0]}')
35 changes: 35 additions & 0 deletions test/prosopo_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import unittest

from pydantic.error_wrappers import ValidationError
from capmonstercloudclient.requests import ProsopoTaskRequest


class ProsopoTaskRequestTest(unittest.TestCase):
websiteUrlExample = "https://example.com"
websiteKeyExample = "prosopo-public-key-123"

def test_prosopo_request_required_fields(self):
required_fields = ["type", "websiteURL", "websiteKey"]
request = ProsopoTaskRequest(
websiteUrl=self.websiteUrlExample,
websiteKey=self.websiteKeyExample
)
task_dictionary = request.getTaskDict()
for f in required_fields:
self.assertTrue(
f in list(task_dictionary.keys()),
msg=f'Required captcha input key "{f}" does not include to request.',
)
self.assertEqual(task_dictionary["type"], "ProsopoTask")

def test_prosopo_missing_fields(self):
base_kwargs = {}
self.assertRaises(ValidationError, ProsopoTaskRequest, **base_kwargs)
base_kwargs.update({"websiteUrl": self.websiteUrlExample})
self.assertRaises(ValidationError, ProsopoTaskRequest, **base_kwargs)
base_kwargs.update({"websiteKey": self.websiteKeyExample})
ProsopoTaskRequest(**base_kwargs)


if __name__ == "__main__":
unittest.main()
49 changes: 49 additions & 0 deletions test/temu_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import unittest

from pydantic.error_wrappers import ValidationError
from capmonstercloudclient.requests import TemuCustomTaskRequest


class TemuCustomTaskRequestTest(unittest.TestCase):
websiteUrlExample = "https://example.com"
userAgentExample = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36"
cookieExample = "sessionid=abc123; path=/; domain=.example.com"

def test_temu_request_required_fields(self):
required_fields = ["type", "class", "websiteURL", "metadata"]
metadata_required_fields = ["cookie"]
request = TemuCustomTaskRequest(
websiteUrl=self.websiteUrlExample, metadata={"cookie": self.cookieExample}
)
task_dictionary = request.getTaskDict()
for f in required_fields:
self.assertTrue(
f in list(task_dictionary.keys()),
msg=f'Required captcha input key "{f}" does not include to request.',
)
for f in metadata_required_fields:
self.assertTrue(
f in list(task_dictionary["metadata"].keys()),
msg=f'Required captcha input key "{f}" does not include to request.',
)

def test_temu_metadata_validation(self):
base_kwargs = {"websiteUrl": self.websiteUrlExample, "metadata": {}}
self.assertRaises(TypeError, TemuCustomTaskRequest, **base_kwargs)
base_kwargs["metadata"]["cookie"] = self.cookieExample
TemuCustomTaskRequest(**base_kwargs)
# Unsupported keys should raise error
base_kwargs["metadata"]["extra"] = "not-allowed"
self.assertRaises(TypeError, TemuCustomTaskRequest, **base_kwargs)

def test_temu_missing_fields(self):
base_kwargs = {}
self.assertRaises(ValidationError, TemuCustomTaskRequest, **base_kwargs)
base_kwargs.update({"websiteUrl": self.websiteUrlExample})
self.assertRaises(ValidationError, TemuCustomTaskRequest, **base_kwargs)
base_kwargs.update({"metadata": {"cookie": self.cookieExample}})
TemuCustomTaskRequest(**base_kwargs)


if __name__ == "__main__":
unittest.main()