python-rucaptcha 6.3.2__py3-none-any.whl → 6.5.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.
- python_rucaptcha/__version__.py +1 -1
- python_rucaptcha/captcha_fox.py +124 -0
- python_rucaptcha/core/base.py +49 -27
- python_rucaptcha/core/config.py +3 -1
- python_rucaptcha/core/enums.py +12 -0
- python_rucaptcha/core/result_handler.py +70 -23
- python_rucaptcha/core/serializer.py +43 -19
- python_rucaptcha/datadome_captcha.py +2 -2
- python_rucaptcha/image_captcha.py +5 -5
- python_rucaptcha/temu_captcha.py +152 -0
- python_rucaptcha/vk_captcha.py +120 -0
- {python_rucaptcha-6.3.2.dist-info → python_rucaptcha-6.5.0.dist-info}/METADATA +6 -6
- {python_rucaptcha-6.3.2.dist-info → python_rucaptcha-6.5.0.dist-info}/RECORD +16 -13
- {python_rucaptcha-6.3.2.dist-info → python_rucaptcha-6.5.0.dist-info}/WHEEL +1 -1
- {python_rucaptcha-6.3.2.dist-info → python_rucaptcha-6.5.0.dist-info/licenses}/LICENSE +1 -1
- {python_rucaptcha-6.3.2.dist-info → python_rucaptcha-6.5.0.dist-info}/top_level.txt +0 -0
python_rucaptcha/__version__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "6.
|
|
1
|
+
__version__ = "6.5.0"
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
from .core.base import BaseCaptcha
|
|
2
|
+
from .core.enums import CaptchaFoxEnm
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CaptchaFox(BaseCaptcha):
|
|
6
|
+
def __init__(
|
|
7
|
+
self,
|
|
8
|
+
websiteURL: str,
|
|
9
|
+
websiteKey: str,
|
|
10
|
+
userAgent: str,
|
|
11
|
+
proxyType: str,
|
|
12
|
+
proxyAddress: str,
|
|
13
|
+
proxyPort: str,
|
|
14
|
+
*args,
|
|
15
|
+
**kwargs,
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
The class is used to work with CaptchaFox.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
rucaptcha_key: User API key
|
|
22
|
+
websiteURL: Full URL of the captcha page
|
|
23
|
+
websiteKey: The value of the `key` parameter.
|
|
24
|
+
It can be found in the page source code or captured in network requests during page loading.
|
|
25
|
+
userAgent: User-Agent of your browser will be used to load the captcha.
|
|
26
|
+
Use only modern browser's User-Agents
|
|
27
|
+
proxyType: Proxy type - `http`, `socks4`, `socks5`
|
|
28
|
+
proxyAddress: Proxy IP address or hostname
|
|
29
|
+
proxyPort: Proxy port
|
|
30
|
+
method: Captcha type
|
|
31
|
+
kwargs: Not required params for task creation request
|
|
32
|
+
|
|
33
|
+
Examples:
|
|
34
|
+
>>> CaptchaFox(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
35
|
+
... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651",
|
|
36
|
+
... websiteKey="sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G",
|
|
37
|
+
... userAgent="Mozilla/5.0 .....",
|
|
38
|
+
... proxyType="socks5",
|
|
39
|
+
... proxyAddress="1.2.3.4",
|
|
40
|
+
... proxyPort="445",
|
|
41
|
+
... ).captcha_handler()
|
|
42
|
+
{
|
|
43
|
+
"errorId":0,
|
|
44
|
+
"status":"ready",
|
|
45
|
+
"solution":{
|
|
46
|
+
"token":"142000f.....er"
|
|
47
|
+
},
|
|
48
|
+
"cost":"0.002",
|
|
49
|
+
"ip":"1.2.3.4",
|
|
50
|
+
"createTime":1692863536,
|
|
51
|
+
"endTime":1692863556,
|
|
52
|
+
"solveCount":0,
|
|
53
|
+
"taskId": 73243152973,
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
>>> await CaptchaFox(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
57
|
+
... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651",
|
|
58
|
+
... websiteKey="sk_xtNxpk6fCdFbxh1_xJeGflSdCE9tn99G",
|
|
59
|
+
... userAgent="Mozilla/5.0 .....",
|
|
60
|
+
... proxyType="socks5",
|
|
61
|
+
... proxyAddress="1.2.3.4",
|
|
62
|
+
... proxyPort="445",
|
|
63
|
+
... ).aio_captcha_handler()
|
|
64
|
+
{
|
|
65
|
+
"errorId":0,
|
|
66
|
+
"status":"ready",
|
|
67
|
+
"solution":{
|
|
68
|
+
"token":"142000f.....er"
|
|
69
|
+
},
|
|
70
|
+
"cost":"0.002",
|
|
71
|
+
"ip":"1.2.3.4",
|
|
72
|
+
"createTime":1692863536,
|
|
73
|
+
"endTime":1692863556,
|
|
74
|
+
"solveCount":0,
|
|
75
|
+
"taskId": 73243152973,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Dict with full server response
|
|
80
|
+
|
|
81
|
+
Notes:
|
|
82
|
+
https://2captcha.com/api-docs/captchafox
|
|
83
|
+
|
|
84
|
+
https://rucaptcha.com/api-docs/captchafox
|
|
85
|
+
"""
|
|
86
|
+
super().__init__(method=CaptchaFoxEnm.CaptchaFoxTask, *args, **kwargs)
|
|
87
|
+
|
|
88
|
+
self.create_task_payload["task"].update(
|
|
89
|
+
{
|
|
90
|
+
"websiteURL": websiteURL,
|
|
91
|
+
"websiteKey": websiteKey,
|
|
92
|
+
"userAgent": userAgent,
|
|
93
|
+
"proxyType": proxyType,
|
|
94
|
+
"proxyAddress": proxyAddress,
|
|
95
|
+
"proxyPort": proxyPort,
|
|
96
|
+
}
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def captcha_handler(self, **kwargs) -> dict:
|
|
100
|
+
"""
|
|
101
|
+
Sync solving method
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
kwargs: additional params for `requests` library
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Dict with full server response
|
|
108
|
+
|
|
109
|
+
Notes:
|
|
110
|
+
Check class docstirng for more info
|
|
111
|
+
"""
|
|
112
|
+
return self._processing_response(**kwargs)
|
|
113
|
+
|
|
114
|
+
async def aio_captcha_handler(self) -> dict:
|
|
115
|
+
"""
|
|
116
|
+
Async solving method
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Dict with full server response
|
|
120
|
+
|
|
121
|
+
Notes:
|
|
122
|
+
Check class docstirng for more info
|
|
123
|
+
"""
|
|
124
|
+
return await self._aio_processing_response()
|
python_rucaptcha/core/base.py
CHANGED
|
@@ -3,7 +3,7 @@ import time
|
|
|
3
3
|
import uuid
|
|
4
4
|
import base64
|
|
5
5
|
import asyncio
|
|
6
|
-
from typing import Optional
|
|
6
|
+
from typing import Any, Optional
|
|
7
7
|
from pathlib import Path
|
|
8
8
|
|
|
9
9
|
import aiohttp
|
|
@@ -30,16 +30,32 @@ class BaseCaptcha:
|
|
|
30
30
|
rucaptcha_key: str,
|
|
31
31
|
method: str,
|
|
32
32
|
sleep_time: int = 10,
|
|
33
|
-
service_type: str = ServiceEnm.TWOCAPTCHA
|
|
34
|
-
**kwargs,
|
|
33
|
+
service_type: ServiceEnm | str = ServiceEnm.TWOCAPTCHA,
|
|
34
|
+
**kwargs: dict[str, Any],
|
|
35
35
|
):
|
|
36
36
|
"""
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
Base class for interacting with CAPTCHA-solving services such as 2Captcha and RuCaptcha.
|
|
38
|
+
|
|
39
|
+
This class handles the setup of request payloads, session configuration, and service-specific
|
|
40
|
+
parameters required to submit CAPTCHA tasks and retrieve their results. It supports optional
|
|
41
|
+
customization of task parameters via keyword arguments and includes retry logic for HTTP requests.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
rucaptcha_key (str):
|
|
45
|
+
API key provided by the CAPTCHA-solving service.
|
|
46
|
+
method (str):
|
|
47
|
+
Type of CAPTCHA to solve (e.g., "ImageToText", "ReCaptchaV2").
|
|
48
|
+
sleep_time (int, optional):
|
|
49
|
+
Time in seconds to wait between polling attempts. Defaults to 10.
|
|
50
|
+
service_type (ServiceEnm | str, optional):
|
|
51
|
+
Service provider to use. Accepts `ServiceEnm.TWOCAPTCHA` or `"rucaptcha"`. Defaults to TWOCAPTCHA.
|
|
52
|
+
**kwargs (dict[str, Any]):
|
|
53
|
+
Optional parameters to be injected into the task payload (e.g., `websiteURL`, `siteKey`, `proxy`).
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> captcha = BaseCaptcha("your-api-key", method="ReCaptchaV2", websiteURL="https://example.com", siteKey="abc123")
|
|
57
|
+
>>> captcha.create_task_payload
|
|
58
|
+
{'clientKey': 'your-api-key', 'task': {'type': 'ReCaptchaV2', 'websiteURL': 'https://example.com', 'siteKey': 'abc123'}}
|
|
43
59
|
"""
|
|
44
60
|
self.result = GetTaskResultResponseSer()
|
|
45
61
|
# assign args to validator
|
|
@@ -48,7 +64,7 @@ class BaseCaptcha:
|
|
|
48
64
|
|
|
49
65
|
# prepare create task payload
|
|
50
66
|
self.create_task_payload = CreateTaskBaseSer(
|
|
51
|
-
clientKey=rucaptcha_key, task=TaskSer(type=method)
|
|
67
|
+
clientKey=rucaptcha_key, task=TaskSer(type=method)
|
|
52
68
|
).to_dict()
|
|
53
69
|
# prepare get task result data payload
|
|
54
70
|
self.get_task_payload = GetTaskResultRequestSer(clientKey=rucaptcha_key)
|
|
@@ -61,7 +77,7 @@ class BaseCaptcha:
|
|
|
61
77
|
self.session.mount("http://", HTTPAdapter(max_retries=RETRIES))
|
|
62
78
|
self.session.mount("https://", HTTPAdapter(max_retries=RETRIES))
|
|
63
79
|
|
|
64
|
-
def _processing_response(self, **kwargs: dict) -> dict:
|
|
80
|
+
def _processing_response(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
65
81
|
"""
|
|
66
82
|
Method processing captcha solving task creation result
|
|
67
83
|
:param kwargs: additional params for Requests library
|
|
@@ -90,13 +106,13 @@ class BaseCaptcha:
|
|
|
90
106
|
url_response=self.params.url_response,
|
|
91
107
|
)
|
|
92
108
|
|
|
93
|
-
def url_open(self, url: str, **kwargs):
|
|
109
|
+
def url_open(self, url: str, **kwargs: dict[str, Any]):
|
|
94
110
|
"""
|
|
95
111
|
Method open links
|
|
96
112
|
"""
|
|
97
113
|
return self.session.get(url=url, **kwargs)
|
|
98
114
|
|
|
99
|
-
async def aio_url_read(self, url: str, **kwargs) -> bytes:
|
|
115
|
+
async def aio_url_read(self, url: str, **kwargs: dict[str, Any]) -> bytes | None:
|
|
100
116
|
"""
|
|
101
117
|
Async method read bytes from link
|
|
102
118
|
"""
|
|
@@ -106,7 +122,7 @@ class BaseCaptcha:
|
|
|
106
122
|
async with session.get(url=url, **kwargs) as resp:
|
|
107
123
|
return await resp.content.read()
|
|
108
124
|
|
|
109
|
-
async def _aio_processing_response(self) -> dict:
|
|
125
|
+
async def _aio_processing_response(self) -> dict[str, Any]:
|
|
110
126
|
"""
|
|
111
127
|
Method processing async captcha solving task creation result
|
|
112
128
|
"""
|
|
@@ -167,23 +183,24 @@ class BaseCaptcha:
|
|
|
167
183
|
|
|
168
184
|
def _body_file_processing(
|
|
169
185
|
self,
|
|
170
|
-
save_format: SaveFormatsEnm,
|
|
186
|
+
save_format: SaveFormatsEnm | str,
|
|
171
187
|
file_path: str,
|
|
172
188
|
file_extension: str = "png",
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
189
|
+
image_key: str = "body",
|
|
190
|
+
captcha_link: str | None = None,
|
|
191
|
+
captcha_file: str | None = None,
|
|
192
|
+
captcha_base64: bytes | None = None,
|
|
193
|
+
**kwargs: dict[str, Any],
|
|
177
194
|
):
|
|
178
195
|
# if a local file link is passed
|
|
179
196
|
if captcha_file:
|
|
180
197
|
self.create_task_payload["task"].update(
|
|
181
|
-
{
|
|
198
|
+
{image_key: base64.b64encode(self._local_file_captcha(captcha_file)).decode("utf-8")}
|
|
182
199
|
)
|
|
183
200
|
# if the file is transferred in base64 encoding
|
|
184
201
|
elif captcha_base64:
|
|
185
202
|
self.create_task_payload["task"].update(
|
|
186
|
-
{
|
|
203
|
+
{image_key: base64.b64encode(captcha_base64).decode("utf-8")}
|
|
187
204
|
)
|
|
188
205
|
# if a URL is passed
|
|
189
206
|
elif captcha_link:
|
|
@@ -192,7 +209,9 @@ class BaseCaptcha:
|
|
|
192
209
|
# according to the value of the passed parameter, select the function to save the image
|
|
193
210
|
if save_format == SaveFormatsEnm.CONST.value:
|
|
194
211
|
self._file_const_saver(content, file_path, file_extension=file_extension)
|
|
195
|
-
self.create_task_payload["task"].update(
|
|
212
|
+
self.create_task_payload["task"].update(
|
|
213
|
+
{image_key: base64.b64encode(content).decode("utf-8")}
|
|
214
|
+
)
|
|
196
215
|
except Exception as error:
|
|
197
216
|
self.result.errorId = 12
|
|
198
217
|
self.result.errorCode = self.NO_CAPTCHA_ERR
|
|
@@ -204,23 +223,24 @@ class BaseCaptcha:
|
|
|
204
223
|
|
|
205
224
|
async def _aio_body_file_processing(
|
|
206
225
|
self,
|
|
207
|
-
save_format: SaveFormatsEnm,
|
|
226
|
+
save_format: SaveFormatsEnm | str,
|
|
208
227
|
file_path: str,
|
|
209
228
|
file_extension: str = "png",
|
|
229
|
+
image_key: str = "body",
|
|
210
230
|
captcha_link: Optional[str] = None,
|
|
211
231
|
captcha_file: Optional[str] = None,
|
|
212
232
|
captcha_base64: Optional[bytes] = None,
|
|
213
|
-
**kwargs,
|
|
233
|
+
**kwargs: dict[str, Any],
|
|
214
234
|
):
|
|
215
235
|
# if a local file link is passed
|
|
216
236
|
if captcha_file:
|
|
217
237
|
self.create_task_payload["task"].update(
|
|
218
|
-
{
|
|
238
|
+
{image_key: base64.b64encode(self._local_file_captcha(captcha_file)).decode("utf-8")}
|
|
219
239
|
)
|
|
220
240
|
# if the file is transferred in base64 encoding
|
|
221
241
|
elif captcha_base64:
|
|
222
242
|
self.create_task_payload["task"].update(
|
|
223
|
-
{
|
|
243
|
+
{image_key: base64.b64encode(captcha_base64).decode("utf-8")}
|
|
224
244
|
)
|
|
225
245
|
# if a URL is passed
|
|
226
246
|
elif captcha_link:
|
|
@@ -229,7 +249,9 @@ class BaseCaptcha:
|
|
|
229
249
|
# according to the value of the passed parameter, select the function to save the image
|
|
230
250
|
if save_format == SaveFormatsEnm.CONST.value:
|
|
231
251
|
self._file_const_saver(content, file_path, file_extension=file_extension)
|
|
232
|
-
self.create_task_payload["task"].update(
|
|
252
|
+
self.create_task_payload["task"].update(
|
|
253
|
+
{image_key: base64.b64encode(content).decode("utf-8")}
|
|
254
|
+
)
|
|
233
255
|
except Exception as error:
|
|
234
256
|
self.result.errorId = 12
|
|
235
257
|
self.result.errorCode = self.NO_CAPTCHA_ERR
|
python_rucaptcha/core/config.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Generator
|
|
2
|
+
|
|
1
3
|
from tenacity import AsyncRetrying, wait_fixed, stop_after_attempt
|
|
2
4
|
from requests.adapters import Retry
|
|
3
5
|
|
|
@@ -8,7 +10,7 @@ APP_KEY = "1899"
|
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
# Connection retry generator
|
|
11
|
-
def attempts_generator(amount: int = 20):
|
|
13
|
+
def attempts_generator(amount: int = 20) -> Generator[int, None, None]:
|
|
12
14
|
"""
|
|
13
15
|
Function generates a generator of length equal to `amount`
|
|
14
16
|
|
python_rucaptcha/core/enums.py
CHANGED
|
@@ -164,3 +164,15 @@ class atbCaptchaEnm(str, MyEnum):
|
|
|
164
164
|
class ProsopoEnm(str, MyEnum):
|
|
165
165
|
ProsopoTask = "ProsopoTask"
|
|
166
166
|
ProsopoTaskProxyless = "ProsopoTaskProxyless "
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class CaptchaFoxEnm(str, MyEnum):
|
|
170
|
+
CaptchaFoxTask = "CaptchaFoxTask"
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class VKCaptchaEnm(str, MyEnum):
|
|
174
|
+
VKCaptchaTask = "VKCaptchaTask"
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class TemuCaptchaEnm(str, MyEnum):
|
|
178
|
+
TemuCaptchaTask = "TemuCaptchaTask"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import time
|
|
2
2
|
import asyncio
|
|
3
3
|
import logging
|
|
4
|
-
from typing import
|
|
4
|
+
from typing import Any
|
|
5
5
|
|
|
6
6
|
import aiohttp
|
|
7
7
|
import requests
|
|
@@ -12,38 +12,81 @@ from .serializer import GetTaskResultRequestSer, GetTaskResultResponseSer
|
|
|
12
12
|
|
|
13
13
|
def get_sync_result(
|
|
14
14
|
get_payload: GetTaskResultRequestSer, sleep_time: int, url_response: str
|
|
15
|
-
) ->
|
|
15
|
+
) -> dict[str, str]:
|
|
16
16
|
"""
|
|
17
|
-
|
|
17
|
+
Periodically sends a synchronous request to a remote service to retrieve the result
|
|
18
|
+
of a CAPTCHA-solving task.
|
|
19
|
+
|
|
20
|
+
This function polls the service using blocking HTTP requests until the CAPTCHA is solved,
|
|
21
|
+
an error is returned, or the task times out. It handles intermediate states and retries
|
|
22
|
+
with a configurable sleep interval between attempts.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
get_payload (GetTaskResultRequestSer):
|
|
26
|
+
Serialized request object containing the task ID and payload data.
|
|
27
|
+
sleep_time (int):
|
|
28
|
+
Time in seconds to wait between polling attempts when the task is still processing.
|
|
29
|
+
url_response (str):
|
|
30
|
+
Endpoint URL to query for the CAPTCHA-solving result.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
dict[str, str]:
|
|
34
|
+
A dictionary containing the final task result. If the task fails or an exception
|
|
35
|
+
occurs, the dictionary includes error details such as status, errorId, errorCode,
|
|
36
|
+
and errorDescription.
|
|
18
37
|
"""
|
|
38
|
+
logging.warning(f"{url_response = }")
|
|
39
|
+
response_ser = GetTaskResultResponseSer(taskId=get_payload.taskId)
|
|
19
40
|
# generator for repeated attempts to connect to the server
|
|
20
41
|
attempts = attempts_generator()
|
|
21
42
|
for _ in attempts:
|
|
22
43
|
try:
|
|
23
44
|
# send a request for the result of solving the captcha
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
)
|
|
27
|
-
logging.warning(f"{captcha_response = }")
|
|
45
|
+
result: dict[str, Any] = requests.post(url_response, json=get_payload.to_dict()).json()
|
|
46
|
+
logging.info(f"Received captcha sync result - {result = }")
|
|
47
|
+
response_ser = GetTaskResultResponseSer(**result, taskId=get_payload.taskId)
|
|
28
48
|
# if the captcha has not been resolved yet, wait
|
|
29
|
-
if
|
|
49
|
+
if response_ser.status == "processing":
|
|
30
50
|
time.sleep(sleep_time)
|
|
31
51
|
continue
|
|
32
|
-
elif
|
|
52
|
+
elif response_ser.status == "ready":
|
|
33
53
|
break
|
|
34
|
-
elif
|
|
35
|
-
return
|
|
54
|
+
elif response_ser.errorId != 0:
|
|
55
|
+
return response_ser.to_dict()
|
|
36
56
|
except Exception as error:
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
response_ser.status = "failed"
|
|
58
|
+
response_ser.errorId = 12
|
|
59
|
+
response_ser.errorCode = "System error"
|
|
60
|
+
response_ser.errorDescription = str(error)
|
|
61
|
+
return response_ser.to_dict()
|
|
39
62
|
|
|
40
63
|
|
|
41
64
|
async def get_async_result(
|
|
42
65
|
get_payload: GetTaskResultRequestSer, sleep_time: int, url_response: str
|
|
43
|
-
) ->
|
|
66
|
+
) -> dict[str, str]:
|
|
44
67
|
"""
|
|
45
|
-
|
|
68
|
+
Periodically sends an asynchronous request to a remote service to retrieve the result
|
|
69
|
+
of a CAPTCHA-solving task.
|
|
70
|
+
|
|
71
|
+
This function polls the service at regular intervals until the CAPTCHA is solved,
|
|
72
|
+
an error occurs, or the task times out. It uses aiohttp for asynchronous HTTP requests
|
|
73
|
+
and handles various response states including 'processing', 'ready', and error conditions.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
get_payload (GetTaskResultRequestSer):
|
|
77
|
+
Serialized request object containing the task ID and payload data.
|
|
78
|
+
sleep_time (int):
|
|
79
|
+
Time in seconds to wait between polling attempts when the task is still processing.
|
|
80
|
+
url_response (str):
|
|
81
|
+
Endpoint URL to query for the CAPTCHA-solving result.
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
dict[str, str]:
|
|
85
|
+
A dictionary containing the final task result if successful or partially failed.
|
|
86
|
+
If an exception occurs during the request, returns a dictionary with error details
|
|
87
|
+
including status, errorId, errorCode, and errorDescription.
|
|
46
88
|
"""
|
|
89
|
+
response_ser = GetTaskResultResponseSer(taskId=get_payload.taskId)
|
|
47
90
|
# generator for repeated attempts to connect to the server
|
|
48
91
|
attempts = attempts_generator()
|
|
49
92
|
async with aiohttp.ClientSession() as session:
|
|
@@ -53,17 +96,21 @@ async def get_async_result(
|
|
|
53
96
|
async with session.post(
|
|
54
97
|
url_response, json=get_payload.to_dict(), raise_for_status=True
|
|
55
98
|
) as resp:
|
|
56
|
-
|
|
57
|
-
|
|
99
|
+
result: dict[str, Any] = await resp.json(content_type=None)
|
|
100
|
+
logging.info(f"Received captcha async result - {result = }")
|
|
101
|
+
response_ser = GetTaskResultResponseSer(**result, taskId=get_payload.taskId)
|
|
58
102
|
|
|
59
103
|
# if the captcha has not been resolved yet, wait
|
|
60
|
-
if
|
|
104
|
+
if response_ser.status == "processing":
|
|
61
105
|
await asyncio.sleep(sleep_time)
|
|
62
106
|
continue
|
|
63
|
-
elif
|
|
107
|
+
elif response_ser.status == "ready":
|
|
64
108
|
break
|
|
65
|
-
elif
|
|
66
|
-
return
|
|
109
|
+
elif response_ser.errorId != 0:
|
|
110
|
+
return response_ser.to_dict()
|
|
67
111
|
except Exception as error:
|
|
68
|
-
|
|
69
|
-
|
|
112
|
+
response_ser.status = "failed"
|
|
113
|
+
response_ser.errorId = 12
|
|
114
|
+
response_ser.errorCode = "System error"
|
|
115
|
+
response_ser.errorDescription = str(error)
|
|
116
|
+
return response_ser.to_dict()
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Any, Literal
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from datetime import date, datetime
|
|
2
4
|
|
|
3
5
|
from msgspec import Struct
|
|
4
6
|
|
|
@@ -7,8 +9,27 @@ from .config import APP_KEY
|
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class MyBaseModel(Struct):
|
|
10
|
-
def to_dict(self):
|
|
11
|
-
|
|
12
|
+
def to_dict(self) -> dict[str, Any]:
|
|
13
|
+
result = {}
|
|
14
|
+
for field in self.__struct_fields__:
|
|
15
|
+
value = getattr(self, field)
|
|
16
|
+
|
|
17
|
+
if isinstance(value, MyBaseModel):
|
|
18
|
+
result[field] = value.to_dict()
|
|
19
|
+
|
|
20
|
+
elif isinstance(value, (list, tuple)) and all(isinstance(el, Struct) for el in value):
|
|
21
|
+
result[field] = [el.to_dict() for el in value]
|
|
22
|
+
|
|
23
|
+
elif isinstance(value, (date, datetime)):
|
|
24
|
+
result[field] = value.isoformat()
|
|
25
|
+
|
|
26
|
+
elif isinstance(value, Decimal):
|
|
27
|
+
result[field] = str(value)
|
|
28
|
+
|
|
29
|
+
else:
|
|
30
|
+
result[field] = value
|
|
31
|
+
|
|
32
|
+
return result
|
|
12
33
|
|
|
13
34
|
|
|
14
35
|
"""
|
|
@@ -22,28 +43,31 @@ class TaskSer(MyBaseModel):
|
|
|
22
43
|
|
|
23
44
|
class CreateTaskBaseSer(MyBaseModel):
|
|
24
45
|
clientKey: str
|
|
25
|
-
task: TaskSer
|
|
46
|
+
task: TaskSer
|
|
26
47
|
languagePool: str = "en"
|
|
27
|
-
callbackUrl: str = None
|
|
48
|
+
callbackUrl: str | None = None
|
|
28
49
|
soft_id: Literal[APP_KEY] = APP_KEY
|
|
29
50
|
|
|
30
51
|
|
|
31
52
|
class GetTaskResultRequestSer(MyBaseModel):
|
|
32
53
|
clientKey: str
|
|
33
|
-
taskId: int = None
|
|
54
|
+
taskId: int | None = None
|
|
34
55
|
|
|
35
56
|
|
|
36
57
|
class CaptchaOptionsSer(MyBaseModel):
|
|
37
58
|
sleep_time: int = 10
|
|
38
|
-
service_type: enums.ServiceEnm = enums.ServiceEnm.TWOCAPTCHA
|
|
59
|
+
service_type: enums.ServiceEnm | str = enums.ServiceEnm.TWOCAPTCHA
|
|
39
60
|
|
|
40
|
-
url_request:
|
|
41
|
-
url_response:
|
|
61
|
+
url_request: str = f"http://api.{enums.ServiceEnm.TWOCAPTCHA.value}.com/2captcha/in.php"
|
|
62
|
+
url_response: str = f"http://api.{enums.ServiceEnm.TWOCAPTCHA.value}.com/2captcha/res.php"
|
|
42
63
|
|
|
43
64
|
def urls_set(self):
|
|
44
65
|
"""
|
|
45
66
|
Set request/response URLs if they not set previously
|
|
46
67
|
"""
|
|
68
|
+
if isinstance(self.service_type, enums.ServiceEnm):
|
|
69
|
+
self.service_type = self.service_type.value
|
|
70
|
+
|
|
47
71
|
if self.service_type == enums.ServiceEnm.DEATHBYCAPTCHA:
|
|
48
72
|
self.url_request = f"http://api.{self.service_type}.com/2captcha/in.php"
|
|
49
73
|
self.url_response = f"http://api.{self.service_type}.com/2captcha/res.php"
|
|
@@ -59,16 +83,16 @@ HTTP API Response
|
|
|
59
83
|
|
|
60
84
|
class GetTaskResultResponseSer(MyBaseModel):
|
|
61
85
|
status: str = "ready"
|
|
62
|
-
solution: dict = None
|
|
63
|
-
cost: float =
|
|
64
|
-
ip: str = None
|
|
65
|
-
createTime: int = None
|
|
66
|
-
endTime: int = None
|
|
67
|
-
solveCount: int = None
|
|
68
|
-
taskId: int = None
|
|
86
|
+
solution: dict[str, str] | None = None
|
|
87
|
+
cost: float = 0.0
|
|
88
|
+
ip: str | None = None
|
|
89
|
+
createTime: int | None = None
|
|
90
|
+
endTime: int | None = None
|
|
91
|
+
solveCount: int | None = None
|
|
92
|
+
taskId: int | None = None
|
|
69
93
|
# control method params
|
|
70
|
-
balance: float = None
|
|
94
|
+
balance: float | None = None
|
|
71
95
|
# error info
|
|
72
96
|
errorId: int = 0
|
|
73
|
-
errorCode: str = None
|
|
74
|
-
errorDescription: str = None
|
|
97
|
+
errorCode: str | None = None
|
|
98
|
+
errorDescription: str | None = None
|
|
@@ -34,7 +34,7 @@ class DataDomeCaptcha(BaseCaptcha):
|
|
|
34
34
|
>>> DataDomeCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
35
35
|
... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651",
|
|
36
36
|
... captchaUrl="https://rucaptcha.com/demo/hcaptcha",
|
|
37
|
-
... userAgent="
|
|
37
|
+
... userAgent="Mozilla/5.0 .....",
|
|
38
38
|
... proxyType="socks5",
|
|
39
39
|
... proxyAddress="1.2.3.4",
|
|
40
40
|
... proxyPort="445",
|
|
@@ -56,7 +56,7 @@ class DataDomeCaptcha(BaseCaptcha):
|
|
|
56
56
|
>>> await DataDomeCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
57
57
|
... websiteURL="3ceb8624-1970-4e6b-91d5-70317b70b651",
|
|
58
58
|
... captchaUrl="https://rucaptcha.com/demo/hcaptcha",
|
|
59
|
-
... userAgent="
|
|
59
|
+
... userAgent="Mozilla/5.0 .....",
|
|
60
60
|
... proxyType="socks5",
|
|
61
61
|
... proxyAddress="1.2.3.4",
|
|
62
62
|
... proxyPort="445",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import shutil
|
|
2
|
-
from typing import Union, Optional
|
|
2
|
+
from typing import Any, Union, Optional
|
|
3
3
|
|
|
4
4
|
from .core.base import BaseCaptcha
|
|
5
5
|
from .core.enums import SaveFormatsEnm, ImageCaptchaEnm
|
|
@@ -181,8 +181,8 @@ class ImageCaptcha(BaseCaptcha):
|
|
|
181
181
|
captcha_link: Optional[str] = None,
|
|
182
182
|
captcha_file: Optional[str] = None,
|
|
183
183
|
captcha_base64: Optional[bytes] = None,
|
|
184
|
-
**kwargs,
|
|
185
|
-
) -> dict:
|
|
184
|
+
**kwargs: dict[str, Any],
|
|
185
|
+
) -> dict[str, Any]:
|
|
186
186
|
"""
|
|
187
187
|
Sync solving method
|
|
188
188
|
|
|
@@ -215,8 +215,8 @@ class ImageCaptcha(BaseCaptcha):
|
|
|
215
215
|
captcha_link: Optional[str] = None,
|
|
216
216
|
captcha_file: Optional[str] = None,
|
|
217
217
|
captcha_base64: Optional[bytes] = None,
|
|
218
|
-
**kwargs,
|
|
219
|
-
) -> dict:
|
|
218
|
+
**kwargs: dict[str, Any],
|
|
219
|
+
) -> dict[str, Any]:
|
|
220
220
|
"""
|
|
221
221
|
Async solving method
|
|
222
222
|
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
from typing import Any, Union
|
|
3
|
+
|
|
4
|
+
from .core.base import BaseCaptcha
|
|
5
|
+
from .core.enums import SaveFormatsEnm, TemuCaptchaEnm
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TemuCaptcha(BaseCaptcha):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
save_format: Union[str, SaveFormatsEnm] = SaveFormatsEnm.TEMP,
|
|
12
|
+
img_clearing: bool = True,
|
|
13
|
+
img_path: str = "PythonRuCaptchaImages",
|
|
14
|
+
*args,
|
|
15
|
+
**kwargs: dict[str, Any],
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Solve TemuImageTask CAPTCHA via 2Captcha/RuCaptcha API.
|
|
19
|
+
|
|
20
|
+
This class creates and monitors TemuImageTask jobs, which require
|
|
21
|
+
a base64‐encoded background image plus an array of movable image
|
|
22
|
+
pieces (parts) in base64 format. It extends BaseCaptcha to handle
|
|
23
|
+
the low‐level request/response workflow.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
save_format (str | SaveFormatsEnm): Where to save temporary images.
|
|
27
|
+
- SaveFormatsEnm.TEMP: use system temp directory
|
|
28
|
+
- SaveFormatsEnm.CONST: keep files in img_path until deletion
|
|
29
|
+
img_clearing (bool): If True and save_format is CONST, delete the
|
|
30
|
+
img_path directory when this instance is destroyed.
|
|
31
|
+
img_path (str): Directory under which to store downloaded or decoded
|
|
32
|
+
images before sending to the API.
|
|
33
|
+
*args: Positional args forwarded to BaseCaptcha constructor (e.g.
|
|
34
|
+
client_key, method override).
|
|
35
|
+
**kwargs: Keyword args forwarded to BaseCaptcha for task creation.
|
|
36
|
+
Common params include:
|
|
37
|
+
- redirectUri: URL to confirm CAPTCHA resolution
|
|
38
|
+
- any other API‐supported parameters
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
>>> captcha = TemuCaptcha(rucaptcha_key="YOUR_API_KEY")
|
|
42
|
+
>>> response = captcha.captcha_handler(
|
|
43
|
+
... parts=["part1_b64", "part2_b64", "part3_b64"],
|
|
44
|
+
... captcha_base64=b"full_image_b64"
|
|
45
|
+
... )
|
|
46
|
+
>>> print(response)
|
|
47
|
+
{
|
|
48
|
+
"errorId": 0,
|
|
49
|
+
"status": "ready",
|
|
50
|
+
"solution": {
|
|
51
|
+
"coordinates": [{"x":155,"y":358}, {"x":152,"y":153}, {"x":251,"y":333}]
|
|
52
|
+
},
|
|
53
|
+
"cost": "0.0012",
|
|
54
|
+
"createTime": 1754563182,
|
|
55
|
+
"endTime": 1754563190,
|
|
56
|
+
"taskId": 80306543329,
|
|
57
|
+
"ip": "46.53.232.76",
|
|
58
|
+
"solveCount": 1
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
Notes:
|
|
62
|
+
https://2captcha.com/api-docs/temu-captcha
|
|
63
|
+
|
|
64
|
+
https://rucaptcha.com/api-docs/temu-captcha
|
|
65
|
+
"""
|
|
66
|
+
super().__init__(method=TemuCaptchaEnm.TemuCaptchaTask, *args, **kwargs)
|
|
67
|
+
|
|
68
|
+
self.save_format = save_format
|
|
69
|
+
self.img_clearing = img_clearing
|
|
70
|
+
self.img_path = img_path
|
|
71
|
+
|
|
72
|
+
def captcha_handler(
|
|
73
|
+
self,
|
|
74
|
+
parts: list[str],
|
|
75
|
+
captcha_link: str | None = None,
|
|
76
|
+
captcha_file: str | None = None,
|
|
77
|
+
captcha_base64: bytes | None = None,
|
|
78
|
+
**kwargs: dict[str, Any],
|
|
79
|
+
) -> dict[str, Any]:
|
|
80
|
+
"""
|
|
81
|
+
Synchronously solve a TemuImageTask.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
parts (list[str]): List of base64‐encoded strings for each
|
|
85
|
+
movable image piece.
|
|
86
|
+
captcha_link (str | None): URL to background image. Overrides
|
|
87
|
+
captcha_file and captcha_base64 if provided.
|
|
88
|
+
captcha_file (str | None): Path to an image file to read & send.
|
|
89
|
+
captcha_base64 (bytes | None): Raw bytes or base64 string of
|
|
90
|
+
the background image.
|
|
91
|
+
**kwargs: Passed through to the HTTP request call (e.g. timeout,
|
|
92
|
+
headers).
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
dict[str, Any]: Full JSON response from the 2Captcha/RuCaptcha API,
|
|
96
|
+
including errorId, taskId, status, solution, cost, times, etc.
|
|
97
|
+
"""
|
|
98
|
+
self.create_task_payload["task"].update({"parts": parts})
|
|
99
|
+
self._body_file_processing(
|
|
100
|
+
save_format=self.save_format,
|
|
101
|
+
file_path=self.img_path,
|
|
102
|
+
captcha_link=captcha_link,
|
|
103
|
+
captcha_file=captcha_file,
|
|
104
|
+
captcha_base64=captcha_base64,
|
|
105
|
+
image_key="image",
|
|
106
|
+
**kwargs,
|
|
107
|
+
)
|
|
108
|
+
if not self.result.errorId:
|
|
109
|
+
return self._processing_response(**kwargs)
|
|
110
|
+
return self.result.to_dict()
|
|
111
|
+
|
|
112
|
+
async def aio_captcha_handler(
|
|
113
|
+
self,
|
|
114
|
+
parts: list[str],
|
|
115
|
+
captcha_link: str | None = None,
|
|
116
|
+
captcha_file: str | None = None,
|
|
117
|
+
captcha_base64: bytes | None = None,
|
|
118
|
+
) -> dict[str, Any]:
|
|
119
|
+
"""
|
|
120
|
+
Asynchronously solve a TemuImageTask.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
parts (list[str]): List of base64‐encoded strings for each
|
|
124
|
+
movable image piece.
|
|
125
|
+
captcha_link (str | None): URL to background image.
|
|
126
|
+
captcha_file (str | None): Path to an image file to read & send.
|
|
127
|
+
captcha_base64 (bytes | None): Raw bytes or base64 string of image.
|
|
128
|
+
**kwargs: Passed through to the async HTTP request call.
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
dict[str, Any]: API response containing task status and solution.
|
|
132
|
+
"""
|
|
133
|
+
self.create_task_payload["task"].update({"parts": parts})
|
|
134
|
+
await self._aio_body_file_processing(
|
|
135
|
+
save_format=self.save_format,
|
|
136
|
+
file_path=self.img_path,
|
|
137
|
+
captcha_link=captcha_link,
|
|
138
|
+
captcha_file=captcha_file,
|
|
139
|
+
captcha_base64=captcha_base64,
|
|
140
|
+
image_key="image",
|
|
141
|
+
**kwargs,
|
|
142
|
+
)
|
|
143
|
+
if not self.result.errorId:
|
|
144
|
+
return await self._aio_processing_response()
|
|
145
|
+
return self.result.to_dict()
|
|
146
|
+
|
|
147
|
+
def __del__(self):
|
|
148
|
+
"""
|
|
149
|
+
Cleanup saved images folder if configured to do so.
|
|
150
|
+
"""
|
|
151
|
+
if self.save_format == SaveFormatsEnm.CONST.value and self.img_clearing:
|
|
152
|
+
shutil.rmtree(self.img_path)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from .core.base import BaseCaptcha
|
|
4
|
+
from .core.enums import VKCaptchaEnm
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class VKCaptcha(BaseCaptcha):
|
|
8
|
+
def __init__(
|
|
9
|
+
self,
|
|
10
|
+
redirectUri: str,
|
|
11
|
+
userAgent: str,
|
|
12
|
+
proxyType: str,
|
|
13
|
+
proxyAddress: str,
|
|
14
|
+
proxyPort: str,
|
|
15
|
+
*args,
|
|
16
|
+
**kwargs: dict[str, Any],
|
|
17
|
+
):
|
|
18
|
+
"""
|
|
19
|
+
The class is used to work with VKCaptchaTask.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
rucaptcha_key: User API key
|
|
23
|
+
redirectUri: The URL that is returned on requests to the captcha API.
|
|
24
|
+
userAgent: User-Agent of your browser will be used to load the captcha.
|
|
25
|
+
Use only modern browser's User-Agents
|
|
26
|
+
proxyType: Proxy type - `http`, `socks4`, `socks5`
|
|
27
|
+
proxyAddress: Proxy IP address or hostname
|
|
28
|
+
proxyPort: Proxy port
|
|
29
|
+
method: Captcha type
|
|
30
|
+
kwargs: Not required params for task creation request
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> VKCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
34
|
+
... redirectUri="https://id.vk.com/not_robot_captcha?domain=vk.com...",
|
|
35
|
+
... userAgent="Mozilla/5.0 .....",
|
|
36
|
+
... proxyType="socks5",
|
|
37
|
+
... proxyAddress="1.2.3.4",
|
|
38
|
+
... proxyPort="445",
|
|
39
|
+
... ).captcha_handler()
|
|
40
|
+
{
|
|
41
|
+
"errorId":0,
|
|
42
|
+
"status":"ready",
|
|
43
|
+
"solution":{
|
|
44
|
+
"token":"142000f.....er"
|
|
45
|
+
},
|
|
46
|
+
"cost":"0.002",
|
|
47
|
+
"ip":"1.2.3.4",
|
|
48
|
+
"createTime":1692863536,
|
|
49
|
+
"endTime":1692863556,
|
|
50
|
+
"solveCount":0,
|
|
51
|
+
"taskId": 73243152973,
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
>>> await VKCaptcha(rucaptcha_key="aa9011f31111181111168611f1151122",
|
|
55
|
+
... redirectUri="https://id.vk.com/not_robot_captcha?domain=vk.com...",
|
|
56
|
+
... userAgent="Mozilla/5.0 .....",
|
|
57
|
+
... proxyType="socks5",
|
|
58
|
+
... proxyAddress="1.2.3.4",
|
|
59
|
+
... proxyPort="445",
|
|
60
|
+
... ).aio_captcha_handler()
|
|
61
|
+
{
|
|
62
|
+
"errorId":0,
|
|
63
|
+
"status":"ready",
|
|
64
|
+
"solution":{
|
|
65
|
+
"token":"142000f.....er"
|
|
66
|
+
},
|
|
67
|
+
"cost":"0.002",
|
|
68
|
+
"ip":"1.2.3.4",
|
|
69
|
+
"createTime":1692863536,
|
|
70
|
+
"endTime":1692863556,
|
|
71
|
+
"solveCount":0,
|
|
72
|
+
"taskId": 73243152973,
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Dict with full server response
|
|
77
|
+
|
|
78
|
+
Notes:
|
|
79
|
+
https://2captcha.com/api-docs/vk-captcha
|
|
80
|
+
|
|
81
|
+
https://rucaptcha.com/api-docs/vk-captcha
|
|
82
|
+
"""
|
|
83
|
+
super().__init__(method=VKCaptchaEnm.VKCaptchaTask, *args, **kwargs)
|
|
84
|
+
|
|
85
|
+
self.create_task_payload["task"].update(
|
|
86
|
+
{
|
|
87
|
+
"websiteURL": redirectUri,
|
|
88
|
+
"userAgent": userAgent,
|
|
89
|
+
"proxyType": proxyType,
|
|
90
|
+
"proxyAddress": proxyAddress,
|
|
91
|
+
"proxyPort": proxyPort,
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def captcha_handler(self, **kwargs: dict[str, Any]) -> dict[str, Any]:
|
|
96
|
+
"""
|
|
97
|
+
Sync solving method
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
kwargs: additional params for `requests` library
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
Dict with full server response
|
|
104
|
+
|
|
105
|
+
Notes:
|
|
106
|
+
Check class docstirng for more info
|
|
107
|
+
"""
|
|
108
|
+
return self._processing_response(**kwargs)
|
|
109
|
+
|
|
110
|
+
async def aio_captcha_handler(self) -> dict[str, Any]:
|
|
111
|
+
"""
|
|
112
|
+
Async solving method
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Dict with full server response
|
|
116
|
+
|
|
117
|
+
Notes:
|
|
118
|
+
Check class docstirng for more info
|
|
119
|
+
"""
|
|
120
|
+
return await self._aio_processing_response()
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-rucaptcha
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.5.0
|
|
4
4
|
Summary: Python 3.9+ RuCaptcha library with AIO module.
|
|
5
5
|
Author-email: AndreiDrang <python-captcha@pm.me>
|
|
6
|
-
License: MIT
|
|
6
|
+
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://andreidrang.github.io/python-rucaptcha/
|
|
8
8
|
Project-URL: Documentation, https://andreidrang.github.io/python-rucaptcha/
|
|
9
9
|
Project-URL: Repository, https://github.com/AndreiDrang/python-rucaptcha
|
|
10
10
|
Project-URL: Issues, https://github.com/AndreiDrang/python-rucaptcha/issues
|
|
11
11
|
Project-URL: Changelog, https://github.com/AndreiDrang/python-rucaptcha/releases
|
|
12
|
-
Keywords: captcha,rucaptcha,2captcha,deathbycaptcha,recaptcha,geetest,hcaptcha,capypuzzle,rotatecaptcha,funcaptcha,keycaptcha,python3,recaptcha,captcha,security,tencent,atb_captcha,python-library,python-rucaptcha,rucaptcha-client,yandex,turnstile,amazon,amazon_waf,friendly-captcha
|
|
13
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
-
Classifier: License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)
|
|
12
|
+
Keywords: captcha,rucaptcha,2captcha,deathbycaptcha,recaptcha,geetest,hcaptcha,capypuzzle,rotatecaptcha,funcaptcha,keycaptcha,python3,recaptcha,captcha,security,tencent,atb_captcha,python-library,python-rucaptcha,rucaptcha-client,yandex,turnstile,amazon,amazon_waf,vk-captcha,fox-captcha,temu-captcha,friendly-captcha
|
|
15
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
16
14
|
Classifier: Programming Language :: Python
|
|
17
15
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -20,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.10
|
|
21
19
|
Classifier: Programming Language :: Python :: 3.11
|
|
22
20
|
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
22
|
Classifier: Framework :: AsyncIO
|
|
24
23
|
Classifier: Operating System :: Unix
|
|
25
24
|
Classifier: Operating System :: Microsoft :: Windows
|
|
@@ -31,6 +30,7 @@ Requires-Dist: requests>=2.21.0
|
|
|
31
30
|
Requires-Dist: aiohttp>=3.9.2
|
|
32
31
|
Requires-Dist: msgspec<0.20,>=0.18
|
|
33
32
|
Requires-Dist: tenacity<10,>=8
|
|
33
|
+
Dynamic: license-file
|
|
34
34
|
|
|
35
35
|
# python-rucaptcha
|
|
36
36
|
|
|
@@ -1,39 +1,42 @@
|
|
|
1
1
|
python_rucaptcha/__init__.py,sha256=odkQWtwx2RgIjD8pw2ZF-KR-5nL0Yu2Wn7Mu2YdBrSk,61
|
|
2
|
-
python_rucaptcha/__version__.py,sha256=
|
|
2
|
+
python_rucaptcha/__version__.py,sha256=gp-lLlwFu2zSdoW-9KJF20SpGpKvzbNC2pWwy2d56R0,22
|
|
3
3
|
python_rucaptcha/amazon_waf.py,sha256=psEg-sym8xQlMPPto1ND_HE0vGV_SRRrDnsKuEbbxNA,3625
|
|
4
4
|
python_rucaptcha/atb_captcha.py,sha256=FuGG9MKTy7f_Sku7WQLZ7TCZ1ISFnu_0h4I3_Qri8JQ,3724
|
|
5
5
|
python_rucaptcha/audio_captcha.py,sha256=Bp6-gF3cTnYL8aeY8k4D4Ii7zPqy1Qo2OFRooSEd0MQ,5600
|
|
6
6
|
python_rucaptcha/bounding_box_captcha.py,sha256=JvJEU1Y0A1SVkJV9jEFgBQ7Nis-vFkTmfUBoHIeTinY,10648
|
|
7
|
+
python_rucaptcha/captcha_fox.py,sha256=sm2cusbBYeOtMMZmZn51o0rtX8SQzPR_6oYmUSVWTCc,4101
|
|
7
8
|
python_rucaptcha/capy_puzzle.py,sha256=ITr1HNALlaKtBkrBht2QtEtP-u6K4-JXpDKKwih7RhA,4973
|
|
8
9
|
python_rucaptcha/control.py,sha256=1KnPaUOVEU7DY6x9-CJvuyV8nFapBcxGRfE9y3dprTo,4993
|
|
9
10
|
python_rucaptcha/coordinates_captcha.py,sha256=lmotaD_dOzo2T9MdcaOgQdB5opL-Hs-ZVUW5nTy-KJ0,9911
|
|
10
11
|
python_rucaptcha/cutcaptcha.py,sha256=glhvQe8Q35ap7J5dyD7hcXkcswG27fkJpYsvyIoTTRg,4050
|
|
11
12
|
python_rucaptcha/cyber_siara_captcha.py,sha256=P8oLkn3UZDuy3XjEyd59cK2QOHYesV6KQ5pMbw2deiA,3909
|
|
12
|
-
python_rucaptcha/datadome_captcha.py,sha256=
|
|
13
|
+
python_rucaptcha/datadome_captcha.py,sha256=lM-zITgqsBS9F4hlkZgkRJjHT8rLct6Yb9FmFIUDpOA,4160
|
|
13
14
|
python_rucaptcha/draw_around_captcha.py,sha256=uUaVk--31p0xh6sqTxtRdtxl0F4ZQ9KeESpmqY1WPR0,11837
|
|
14
15
|
python_rucaptcha/friendly_captcha.py,sha256=c5sbhAHq5zPlk2-5gGcoPhjslEkBT24EtFip2S8cGao,4232
|
|
15
16
|
python_rucaptcha/fun_captcha.py,sha256=iU_twFRGKjeQPPBXfcqEK0d0kffNaCxPpHrihzBRg5w,3518
|
|
16
17
|
python_rucaptcha/gee_test.py,sha256=lxhqWm2uWWS_uQK5ufArjr-508pfecDYAEh4Ct-FthI,8583
|
|
17
18
|
python_rucaptcha/grid_captcha.py,sha256=ssfl1BnBzxR1CHGdnjEG4wunTvEk9NvGIoiXQWHrt20,9001
|
|
18
19
|
python_rucaptcha/hcaptcha.py,sha256=7i0S7A4LvBePUgVsCGpay8733Ve2rzjiZcR-CMTwa8s,3732
|
|
19
|
-
python_rucaptcha/image_captcha.py,sha256=
|
|
20
|
+
python_rucaptcha/image_captcha.py,sha256=mSLmqAbN2hO47FA-lm4q7PNakBKeEv1-Yt6jDF_q9-s,8448
|
|
20
21
|
python_rucaptcha/key_captcha.py,sha256=hufzqYEb3ZR23AsLWIL82HFTJnA8JsOJtXUA92b9tws,4017
|
|
21
22
|
python_rucaptcha/lemin_captcha.py,sha256=inEq-UWUbMX9aZdpyZBGRER-67YCS3PqxnDIpPAzmqM,4086
|
|
22
23
|
python_rucaptcha/mt_captcha.py,sha256=usQK_LR9piDQM7lxkZbSKi6KIXedIBYyytKJBJe8jNg,3415
|
|
23
24
|
python_rucaptcha/prosopo.py,sha256=kZLn_iZsm1mzQ8pNuPAzbfNfH8UBnLg1iy996Ti3nMo,3516
|
|
24
25
|
python_rucaptcha/re_captcha.py,sha256=XyJncVbmaV5jZR-qxZ1JdFum10W5yk5zeVrDbZ5dSKo,6083
|
|
25
26
|
python_rucaptcha/rotate_captcha.py,sha256=o9y9SVLPI6G6NqK1siv3xbOSYm7PGNquJbbhXuxTIDE,6278
|
|
27
|
+
python_rucaptcha/temu_captcha.py,sha256=Uny8OY-g4Nbl4_X8wZM8TsZzivG1r12AuPhYpdIM-fI,5830
|
|
26
28
|
python_rucaptcha/tencent.py,sha256=f7lnRBUl-X5rL2FNRnrlh9ITRCcerTaS6XGUXnQdnOA,3649
|
|
27
29
|
python_rucaptcha/text_captcha.py,sha256=lZDPwTgpMLvqvfjPbT-uNuNu6JRdWTEE9qfjnRQ5y-A,3708
|
|
28
30
|
python_rucaptcha/turnstile.py,sha256=VpmYGYJoRVX6NHipO6-g2U5EpwxZToAZRmULj4ReDD0,3686
|
|
31
|
+
python_rucaptcha/vk_captcha.py,sha256=kpVQsyhd2eS9WqoK_30e30vOA3EBKrHcBwjCf4LHw1w,3837
|
|
29
32
|
python_rucaptcha/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
python_rucaptcha/core/base.py,sha256=
|
|
31
|
-
python_rucaptcha/core/config.py,sha256=
|
|
32
|
-
python_rucaptcha/core/enums.py,sha256=
|
|
33
|
-
python_rucaptcha/core/result_handler.py,sha256=
|
|
34
|
-
python_rucaptcha/core/serializer.py,sha256=
|
|
35
|
-
python_rucaptcha-6.
|
|
36
|
-
python_rucaptcha-6.
|
|
37
|
-
python_rucaptcha-6.
|
|
38
|
-
python_rucaptcha-6.
|
|
39
|
-
python_rucaptcha-6.
|
|
33
|
+
python_rucaptcha/core/base.py,sha256=nCQ3tU1-8xV4t90gQjeFzWLjNZ88SanCRteg-HBQ6gI,10706
|
|
34
|
+
python_rucaptcha/core/config.py,sha256=ocoPeMXfn-2iqaqcjewZWfRpu73D5dY2AL2zqXVrG9Q,613
|
|
35
|
+
python_rucaptcha/core/enums.py,sha256=qGIM2e0Acz2i35uZVTrVIcrQ7dRobuSWzh-ojs9rDCE,4135
|
|
36
|
+
python_rucaptcha/core/result_handler.py,sha256=gSblrOEe-tJ_kfvNSsN1lbnV8-9Oh2RGTZu6LDzl9M0,5118
|
|
37
|
+
python_rucaptcha/core/serializer.py,sha256=xFem0kEvbjn9nOwXyc5p9ziY5-c2P1pe-JmXnjs9exI,2695
|
|
38
|
+
python_rucaptcha-6.5.0.dist-info/licenses/LICENSE,sha256=ZQeZG4IrO4YywdTal32gqyyrwdMlrMa3EwJCbgM8b2k,1063
|
|
39
|
+
python_rucaptcha-6.5.0.dist-info/METADATA,sha256=A_Xixc0I07xGPbL9CZ_Hce2stb8kAPoZamwcBIIYglY,6659
|
|
40
|
+
python_rucaptcha-6.5.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
41
|
+
python_rucaptcha-6.5.0.dist-info/top_level.txt,sha256=Eu_atEB79Y7jCsfXPcXF5N8OLt6kKVbvhuRsI1BmSWM,17
|
|
42
|
+
python_rucaptcha-6.5.0.dist-info/RECORD,,
|
|
File without changes
|