capmonstercloudclient 3.1.0__py3-none-any.whl → 3.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,7 +30,9 @@ _instance_config = (
30
30
  ((AmazonWafRequest,), getAmazonWafTimeouts),
31
31
  ((BinanceTaskRequest,), getBinanceTimeouts),
32
32
  ((ImpervaCustomTaskRequest,), getImpervaTimeouts),
33
- ((RecognitionComplexImageTaskRequest), getCITTimeouts)
33
+ ((RecognitionComplexImageTaskRequest), getCITTimeouts),
34
+ ((MTCaptchaRequest), getImage2TextTimeouts),
35
+ ((YidunRequest), getImage2TextTimeouts),
34
36
  )
35
37
 
36
38
 
@@ -79,7 +81,9 @@ class CapMonsterClient:
79
81
  BinanceTaskRequest,
80
82
  ImpervaCustomTaskRequest,
81
83
  TurnstileRequest,
82
- RecognitionComplexImageTaskRequest],
84
+ RecognitionComplexImageTaskRequest,
85
+ MTCaptchaRequest,
86
+ YidunRequest],
83
87
  ) -> Dict[str, str]:
84
88
  '''
85
89
  Non-blocking method for captcha solving.
@@ -13,12 +13,8 @@ class DataDomeCustomTaskRequest(CustomTaskRequestBase):
13
13
  if value.get('captchaUrl') and value.get('htmlPageBase64'):
14
14
  raise TypeError(f'Expected only one of [captchaUrl, htmlPageBase64]')
15
15
  elif value.get('captchaUrl'):
16
- if not isinstance(value.get('captchaUrl'), str):
17
- raise TypeError(f'Expect that type imagesUrls array will be <str>, got {type(value.get("captchaUrl"))}')
18
16
  return {i: value[i] for i in value if i != 'htmlPageBase64'}
19
17
  elif value.get('htmlPageBase64'):
20
- if not isinstance(value.get('htmlPageBase64'), str):
21
- raise TypeError(f'Expect that type imagesUrls array will be <str>, got {type(value.get("htmlPageBase64"))}')
22
18
  return {i: value[i] for i in value if i != 'captchaUrl'}
23
19
  else:
24
20
  raise TypeError(f'Expected one of [captchaUrl, htmlPageBase64]')
@@ -10,74 +10,55 @@ class HcaptchaComplexImageTaskRequest(ComplexImageTaskRequestBase):
10
10
  metadata : Dict[str, str]
11
11
  exampleImageUrls: Optional[List[str]] = None
12
12
  exampleImagesBase64: Optional[List[str]] = None
13
-
13
+
14
+ @staticmethod
15
+ def _validate_image_array(value, field_name, max_images):
16
+ """Helper method to validate image array"""
17
+ if value is None:
18
+ return value
19
+ if not isinstance(value, (list, tuple)):
20
+ raise TypeError(f'Expect that type {field_name} array will be <list> or <tuple>, got {type(value)}')
21
+
22
+ if not len(value):
23
+ if 'base64' in field_name.lower():
24
+ raise ZeroImagesErrors(f'At least one image base64 expected, got {len(value)}')
25
+ else:
26
+ raise ZeroImagesErrors(f'At least one image url expected, got {len(value)}')
27
+
28
+ if len(value) > max_images:
29
+ raise NumbersImagesErrors(f'Maximum number of images in list {max_images}, got {len(value)}')
30
+
31
+ contain_types = [isinstance(x, str) for x in value]
32
+ if not all(contain_types):
33
+ if 'base64' in field_name.lower():
34
+ raise TypeError(f'Next images from imagesBase64 array are not string: {contain_types}')
35
+ else:
36
+ raise TypeError(f'Next images from imagesUrls array are not string: {contain_types}')
37
+ return value
38
+
14
39
  @validator('metadata')
15
40
  def validate_metadata(cls, value):
16
41
  if value.get('Task') is None:
17
- raise TaskNotDefinedError(f'Expect that task will be defined.')
42
+ raise TaskNotDefinedError('Expect that task will be defined.')
18
43
  else:
19
44
  return value
20
45
 
21
46
  @validator('exampleImageUrls')
22
- def validate_urls_array(cls, value):
23
- if value is not None:
24
- if not isinstance(value, (list, tuple)):
25
- raise TypeError(f'Expect that type exampleImageUrls array will be <list> or <tuple>, got {type(value)}')
26
- elif len(value) > 1:
27
- raise NumbersImagesErrors(f'Maximum number of images in list 1, got {len(value)}')
28
- elif not len(value):
29
- raise ZeroImagesErrors(f'At least one image url expected, got {len(value)}')
30
- # Check for each element type
31
- contain_types = [isinstance(x, str) for x in value]
32
- if not all(contain_types):
33
- raise TypeError(f'Next images from imagesUrls array are not string: {contain_types}')
34
- return value
47
+ def validate_example_image_urls(cls, value):
48
+ return cls._validate_image_array(value, 'exampleImageUrls', 1)
35
49
 
36
50
  @validator('exampleImagesBase64')
37
- def validate_urls_array(cls, value):
38
- if value is not None:
39
- if not isinstance(value, (list, tuple)):
40
- raise TypeError(f'Expect that type exampleImagesBase64 array will be <list> or <tuple>, got {type(value)}')
41
- elif len(value) > 1:
42
- raise NumbersImagesErrors(f'Maximum number of images in list 1, got {len(value)}')
43
- elif not len(value):
44
- raise ZeroImagesErrors(f'At least one image base64 expected, got {len(value)}')
45
- # Check for each element type
46
- contain_types = [isinstance(x, str) for x in value]
47
- if not all(contain_types):
48
- raise TypeError(f'Next images from imagesBase64 array are not string: {contain_types}')
49
- return value
51
+ def validate_example_images_base64(cls, value):
52
+ return cls._validate_image_array(value, 'exampleImagesBase64', 1)
50
53
 
51
54
  @validator('imagesUrls')
52
- def validate_urls_array(cls, value):
53
- if value is not None:
54
- if not isinstance(value, (list, tuple)):
55
- raise TypeError(f'Expect that type imagesUrls array will be <list> or <tuple>, got {type(value)}')
56
- elif len(value) > 18:
57
- raise NumbersImagesErrors(f'Maximum number of images in list 18, got {len(value)}')
58
- elif not len(value):
59
- raise ZeroImagesErrors(f'At least one image url expected, got {len(value)}')
60
- # Check for each element type
61
- contain_types = [isinstance(x, str) for x in value]
62
- if not all(contain_types):
63
- raise TypeError(f'Next images from imagesUrls array are not string: {contain_types}')
64
- return value
55
+ def validate_images_urls(cls, value):
56
+ return cls._validate_image_array(value, 'imagesUrls', 18)
65
57
 
66
58
  @validator('imagesBase64')
67
- def validate_images_array(cls, value):
68
- if value is not None:
69
- if not isinstance(value, (list, tuple)):
70
- raise TypeError(f'Expect that type imagesBase64 array will be <list> or <tuple>, got {type(value)}')
71
- elif len(value) > 18:
72
- raise NumbersImagesErrors(f'Maximum number of images in list 18, got {len(value)}')
73
- elif not len(value):
74
- raise ZeroImagesErrors(f'At least one image base64 expected, got {len(value)}')
75
- # Check for each element type
76
- contain_types = [isinstance(x, str) for x in value]
77
- if not all(contain_types):
78
- raise TypeError(f'Next images from imagesBase64 array are not string: {contain_types}')
79
- return value
80
-
59
+ def validate_images_base64(cls, value):
60
+ return cls._validate_image_array(value, 'imagesBase64', 18)
61
+
81
62
  def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
82
63
 
83
64
  task = {}
@@ -86,7 +67,7 @@ class HcaptchaComplexImageTaskRequest(ComplexImageTaskRequestBase):
86
67
 
87
68
  # fill with images
88
69
  if self.imagesBase64 is None and self.imagesUrls is None:
89
- raise ZeroImagesErrors(f'Expect at least one of array(imageBase64 or imageUrls) to contain images.')
70
+ raise ZeroImagesErrors('Expect at least one of array(imageBase64 or imageUrls) to contain images.')
90
71
 
91
72
  if self.imagesUrls is not None:
92
73
  task['imageUrls'] = self.imagesUrls
@@ -113,4 +94,3 @@ class HcaptchaComplexImageTaskRequest(ComplexImageTaskRequestBase):
113
94
 
114
95
  return task
115
96
 
116
-
@@ -0,0 +1,32 @@
1
+ from typing import Dict, Optional, Union
2
+ from pydantic import Field
3
+ from .baseRequestWithProxy import BaseRequestWithProxy
4
+
5
+
6
+ class MTCaptchaRequest(BaseRequestWithProxy):
7
+ type: str = Field(default="MTCaptchaTask")
8
+ websiteUrl: str
9
+ websiteKey: str
10
+ pageAction: Optional[str] = Field(default=None)
11
+ isInvisible: Optional[bool] = Field(default=None)
12
+ userAgent: Optional[str] = Field(default=None)
13
+
14
+ def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
15
+ task = {}
16
+ task["type"] = self.type
17
+ task["websiteURL"] = self.websiteUrl
18
+ task["websiteKey"] = self.websiteKey
19
+ if self.pageAction is not None:
20
+ task["pageAction"] = self.pageAction
21
+ if self.isInvisible is not None:
22
+ task["isInvisible"] = self.isInvisible
23
+ if self.userAgent is not None:
24
+ task["userAgent"] = self.userAgent
25
+ if self.proxy:
26
+ task["proxyType"] = self.proxy.proxyType
27
+ task["proxyAddress"] = self.proxy.proxyAddress
28
+ task["proxyPort"] = self.proxy.proxyPort
29
+ task["proxyLogin"] = self.proxy.proxyLogin
30
+ task["proxyPassword"] = self.proxy.proxyPassword
31
+
32
+ return task
@@ -1,4 +1,4 @@
1
- from typing import Dict, Optional
1
+ from typing import Dict, Optional, Union
2
2
  from pydantic import Field, validator, model_validator
3
3
  from .baseRequestWithProxy import BaseRequestWithProxy
4
4
 
@@ -32,6 +32,11 @@ class TurnstileRequest(BaseRequestWithProxy):
32
32
  raise RuntimeError(f'Expect that "htmlPageBase64" will be filled ' \
33
33
  f'when cloudflareTaskType is "cf_clearance"')
34
34
 
35
+ if self.get('proxy') is None:
36
+ if self.get('cloudflareTaskType') == 'cf_clearance':
37
+ raise RuntimeError(f'You are working using queries, and you need cf_clearance cookies ' \
38
+ f'it is required that you need your proxies.')
39
+
35
40
  if self.get('cloudflareTaskType') == 'token':
36
41
  for field in ['pageAction', 'pageData', 'data']:
37
42
  if self.get(field) is None:
@@ -43,9 +48,34 @@ class TurnstileRequest(BaseRequestWithProxy):
43
48
  if self.get('userAgent') is None:
44
49
  raise RuntimeError(f'Expect that userAgent will be filled ' \
45
50
  f'when cloudflareTaskType specified.')
46
-
51
+
47
52
  return self
48
53
 
49
- def getTaskDict(self) -> Dict[str, str]:
50
- return {k: v for k, v in self.model_dump().items() if v is not None}
54
+ def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
55
+ task = {}
56
+ task['type'] = self.type
57
+ task['websiteURL'] = self.websiteURL
58
+ task['websiteKey'] = self.websiteKey
59
+ if self.pageAction is not None:
60
+ task['pageAction'] = self.pageAction
61
+ if self.data is not None:
62
+ task['data'] = self.data
63
+ if self.pageData is not None:
64
+ task['pageData'] = self.pageData
65
+ if self.userAgent is not None:
66
+ task['userAgent'] = self.userAgent
67
+ if self.cloudflareTaskType is not None:
68
+ task['cloudflareTaskType'] = self.cloudflareTaskType
69
+ if self.htmlPageBase64 is not None:
70
+ task['htmlPageBase64'] = self.htmlPageBase64
71
+ if self.apiJsUrl is not None:
72
+ task['apiJsUrl'] = self.apiJsUrl
73
+ if self.proxy:
74
+ task['proxyType'] = self.proxy.proxyType
75
+ task['proxyAddress'] = self.proxy.proxyAddress
76
+ task['proxyPort'] = self.proxy.proxyPort
77
+ task['proxyLogin'] = self.proxy.proxyLogin
78
+ task['proxyPassword'] = self.proxy.proxyPassword
79
+
80
+ return task
51
81
 
@@ -0,0 +1,26 @@
1
+ from typing import Dict, Optional, Union
2
+ from pydantic import Field
3
+ from .baseRequestWithProxy import BaseRequestWithProxy
4
+
5
+
6
+ class YidunRequest(BaseRequestWithProxy):
7
+ type: str = Field(default="YidunTask")
8
+ websiteUrl: str
9
+ websiteKey: str
10
+ userAgent: Optional[str] = Field(default=None)
11
+
12
+ def getTaskDict(self) -> Dict[str, Union[str, int, bool]]:
13
+ task = {}
14
+ task["type"] = self.type
15
+ task["websiteURL"] = self.websiteUrl
16
+ task["websiteKey"] = self.websiteKey
17
+ if self.userAgent is not None:
18
+ task["userAgent"] = self.userAgent
19
+ if self.proxy:
20
+ task["proxyType"] = self.proxy.proxyType
21
+ task["proxyAddress"] = self.proxy.proxyAddress
22
+ task["proxyPort"] = self.proxy.proxyPort
23
+ task["proxyLogin"] = self.proxy.proxyLogin
24
+ task["proxyPassword"] = self.proxy.proxyPassword
25
+
26
+ return task
@@ -18,6 +18,9 @@ from .AmazonWafRequest import AmazonWafRequest
18
18
  from .BinanceTaskRequest import BinanceTaskRequest
19
19
  from .ImpervaCustomTaskRequest import ImpervaCustomTaskRequest
20
20
  from .RecognitionComplexImageTaskRequest import RecognitionComplexImageTaskRequest
21
+ from .MTCaptchaRequest import MTCaptchaRequest
22
+ from .YidunRequest import YidunRequest
23
+ from .proxy_info import ProxyInfo
21
24
 
22
25
 
23
26
  REQUESTS = ['RecaptchaV2EnterpiseRequest', 'RecaptchaV2Request', 'RecaptchaV3ProxylessRequest', 'RecaptchaComplexImageTaskRequest'
@@ -1 +1 @@
1
- 3.1.0
1
+ 3.2.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: capmonstercloudclient
3
- Version: 3.1.0
3
+ Version: 3.2.0
4
4
  Summary: Official CapMonsterCloud Client: https://capmonster.cloud/
5
5
  Home-page: https://github.com/ZennoLab/capmonstercloud-client-python
6
6
  Author: Andrey Ilyin
@@ -1,4 +1,4 @@
1
- capmonstercloudclient/CapMonsterCloudClient.py,sha256=0IWzHunUgTsD2QqR1gbOuil4vMEDC5D4g5561W8Zl5o,9101
1
+ capmonstercloudclient/CapMonsterCloudClient.py,sha256=0YRJepYoyD2P_nwltTlrkD_1l69tc84iScycw-bFY_M,9326
2
2
  capmonstercloudclient/GetResultTimeouts.py,sha256=wgCXitjTsC6BYP3xjLqpW58xS55hsbqcf28IylxRS1E,1661
3
3
  capmonstercloudclient/__init__.py,sha256=XkfS_v1jZUx7HTUhdi-5uiZhvjvd9aLvt31jXXpVxeA,92
4
4
  capmonstercloudclient/captchaResult.py,sha256=qBRmadO9n6DMnG-IwJSzZEt54Z5OAXvWIC5Jvzd7OfI,314
@@ -6,34 +6,36 @@ capmonstercloudclient/clientOptions.py,sha256=kd2CXnmz2_crZd85SoOHOjp1zBklEbu9OW
6
6
  capmonstercloudclient/exceptions.py,sha256=9iTzO0ymBo7eAJwv7qtoPZTmb4yaQ6Fvi69VqBXTcwk,925
7
7
  capmonstercloudclient/requestController.py,sha256=9t31zNN4z1UBw4Q0N9ZIy47CwSQl2VaxD1eqDTDIr9w,497
8
8
  capmonstercloudclient/utils.py,sha256=hP090ddzDCLknr5wGCR7sIALDbOtWbiSzgZPvPnDC3c,609
9
- capmonstercloudclient/version.txt,sha256=hORQlST9mZYLrbrFUlSvzqMw16EOq9al8dy0X8OMP0A,5
9
+ capmonstercloudclient/version.txt,sha256=ojyOaxZvgxpbs8G94LZrGFxuPlkFKS6Kgq_q-EF6VHY,5
10
10
  capmonstercloudclient/requests/AmazonWafRequest.py,sha256=8SrbFTcZg3YI-Fac19Ap1iVT3n3mFBFKB38K1Y9QrWo,1195
11
11
  capmonstercloudclient/requests/BasiliskCustomTaskRequest.py,sha256=GJ7OCsdF-xlnsV6kOI3fdb8C7CyLE7Kr5W4oK_nGn18,913
12
12
  capmonstercloudclient/requests/BinanceTaskRequest.py,sha256=9kD9y_4hZYXKOj2AYoiv--Z0De0Y7JChF9X09snMbZs,1013
13
13
  capmonstercloudclient/requests/ComplexImageTaskBase.py,sha256=7784BontlIxldtIEy9O4Id1nRo6abGpNJZl5z2KwusY,675
14
14
  capmonstercloudclient/requests/CustomTaskRequestBase.py,sha256=_e9pz7EmI-9cjIvvJOmWuEdimxuRoUnq5tvO5_S92IQ,539
15
- capmonstercloudclient/requests/DataDomeCustomTaskRequest.py,sha256=ghsRcRmccdEh_DVsORG4IwnvydSP_pJLK59xPHmbE8Q,2068
15
+ capmonstercloudclient/requests/DataDomeCustomTaskRequest.py,sha256=Cyh9SPR4ilwNbfXY6UmHYV0fnPYFGBNzo630L8Oewfc,1696
16
16
  capmonstercloudclient/requests/FuncaptchaComplexImageTask.py,sha256=5Mo9mBAXUuAx2Cv_lGfNaZLrsEwfrqxSz3ZPDy2KXvY,3074
17
17
  capmonstercloudclient/requests/FuncaptchaRequest.py,sha256=R9BJQTR82Awet2cKAklcsb7pFHUbZD1S816mO647lMg,1133
18
18
  capmonstercloudclient/requests/GeetestRequest.py,sha256=ZHwlHLaL68xa6TJk9LYwtQOcCxW6Xk0fu0ZlB7gxPKI,2034
19
- capmonstercloudclient/requests/HcaptchaComplexImageTask.py,sha256=HGVJefD2h9GX4ILyCYNrew64ZkpFGf0UG1hxXIrg7v0,5240
19
+ capmonstercloudclient/requests/HcaptchaComplexImageTask.py,sha256=1Oee7p2hWVnF7wVonWJ8Y3q-UhrSNX2gNiLjd1O0v2Y,3758
20
20
  capmonstercloudclient/requests/HcaptchaRequest.py,sha256=sa0FRLf-H-9MWlFrowMNED19EfkI_xbkNC6CMsR_pwg,1502
21
21
  capmonstercloudclient/requests/ImageToTextRequest.py,sha256=1F7Gzq0qTuxjbl9XfFbszQUHNG7W5PthuiJBAO_VEug,2481
22
22
  capmonstercloudclient/requests/ImpervaCustomTaskRequest.py,sha256=YUY_wC8yv16rZGK_VTCbqzj1rMxbUeJrxAE-7IT2AYY,1777
23
+ capmonstercloudclient/requests/MTCaptchaRequest.py,sha256=dn2OxdB5Ro8c01Z4NplHKOVbYsA_o2hH8QCNFVLIzfY,1212
23
24
  capmonstercloudclient/requests/RecaptchaComplexImageTask.py,sha256=v7cqgTF5L2ADYHoskveB8v2h6cW68Av60tH7ek3hWNg,3281
24
25
  capmonstercloudclient/requests/RecaptchaV2EnterpiseRequest.py,sha256=TiIV0-SWLUT4mwK8ZzChL3FCZwaizzVsZhzZo0KRgfk,1121
25
26
  capmonstercloudclient/requests/RecaptchaV2Request.py,sha256=wq9IFNehhcVZ-bDzu1Yfq1oD_4I4KbtwRdUwwNaQNm8,1219
26
27
  capmonstercloudclient/requests/RecaptchaV3ProxylessRequest.py,sha256=0IYCgNruZrVr0L7E4dfHE_Pv8DimkrH4PBkEPEv7dxw,1078
27
28
  capmonstercloudclient/requests/RecognitionComplexImageTaskRequest.py,sha256=TzWoKqEAOrgll1LlEFTzB_7dbOKJywRScZCpLUgSMS8,1970
28
29
  capmonstercloudclient/requests/TenDiCustomTaskRequest.py,sha256=OIhLd_ODD6MsvBl6wSVcOHuBn0rv-Cg4sEFd6Y0zrY4,903
29
- capmonstercloudclient/requests/TurnstileRequest.py,sha256=-nB7ULt2qke4z4_m7rUzhL1wjjMXwJ3NS_rDGM3UBtY,2199
30
- capmonstercloudclient/requests/__init__.py,sha256=svvVJKE6X1C6jpwgOlI84QYICAT_gNpUu-qZl1clNhA,1824
30
+ capmonstercloudclient/requests/TurnstileRequest.py,sha256=Z15aoC6IhUCBc1CUAlEd6tpfwk4UK4b-EzL1HbHQqw4,3510
31
+ capmonstercloudclient/requests/YidunRequest.py,sha256=QwHal52oTAzLJOvDVfSqyb1hKvhbcgABdFlw9Il_Y1w,917
32
+ capmonstercloudclient/requests/__init__.py,sha256=M80ammbzgrQMl44UUXDa0YlUcsnnN2ncUYM3SeEitHI,1944
31
33
  capmonstercloudclient/requests/baseRequest.py,sha256=wHYDtEoJbAiJQDFOKWSezLvlxTLO9LCD5v7t3dvfgSg,317
32
34
  capmonstercloudclient/requests/baseRequestWithProxy.py,sha256=RwF1FXH19T9IyR4bbj-fC73huAgKewtJMJy2IvWurpA,230
33
35
  capmonstercloudclient/requests/enums.py,sha256=itJ95OEzwous9zOe4n411w5LeBw4yCpCCTMomlT3LU0,1000
34
36
  capmonstercloudclient/requests/proxy_info.py,sha256=ctmxvykMflsym-snO449t9mOZ1jxpUFTnxcI8koACUw,678
35
- capmonstercloudclient-3.1.0.dist-info/licenses/LICENSE,sha256=VIIsA4331arqJBp_QqiY2cjVszTfuSt3I7uEUr4ouYw,1065
36
- capmonstercloudclient-3.1.0.dist-info/METADATA,sha256=-YLk6fDhT21bMEWTPU6KzVl6phno9JZ1Ap3dUUXYBk4,5462
37
- capmonstercloudclient-3.1.0.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
38
- capmonstercloudclient-3.1.0.dist-info/top_level.txt,sha256=_rR6-Wb02PxobU8D33r_0OONJgybsros2e9H8opYGnA,22
39
- capmonstercloudclient-3.1.0.dist-info/RECORD,,
37
+ capmonstercloudclient-3.2.0.dist-info/licenses/LICENSE,sha256=VIIsA4331arqJBp_QqiY2cjVszTfuSt3I7uEUr4ouYw,1065
38
+ capmonstercloudclient-3.2.0.dist-info/METADATA,sha256=MXa6d5fCzH3-Q09lnN4nr1WBav0wzkaa8WGS-qpcu5I,5462
39
+ capmonstercloudclient-3.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ capmonstercloudclient-3.2.0.dist-info/top_level.txt,sha256=_rR6-Wb02PxobU8D33r_0OONJgybsros2e9H8opYGnA,22
41
+ capmonstercloudclient-3.2.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5