python-rucaptcha 6.4.0__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.
@@ -1 +1 @@
1
- __version__ = "6.4.0"
1
+ __version__ = "6.5.0"
@@ -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.value,
34
- **kwargs,
33
+ service_type: ServiceEnm | str = ServiceEnm.TWOCAPTCHA,
34
+ **kwargs: dict[str, Any],
35
35
  ):
36
36
  """
37
- :param rucaptcha_key: User API key
38
- :param method: Captcha type
39
- :param sleep_time: Time to wait for captcha solution
40
- :param service_type: URL with which the program will work, "2captcha" option is possible (standard)
41
- and "rucaptcha"
42
- :param kwargs: Designed to pass OPTIONAL parameters to the payload for a request to RuCaptcha
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).to_dict()
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
- captcha_link: Optional[str] = None,
174
- captcha_file: Optional[str] = None,
175
- captcha_base64: Optional[bytes] = None,
176
- **kwargs,
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
- {"body": base64.b64encode(self._local_file_captcha(captcha_file)).decode("utf-8")}
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
- {"body": base64.b64encode(captcha_base64).decode("utf-8")}
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({"body": base64.b64encode(content).decode("utf-8")})
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
- {"body": base64.b64encode(self._local_file_captcha(captcha_file)).decode("utf-8")}
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
- {"body": base64.b64encode(captcha_base64).decode("utf-8")}
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({"body": base64.b64encode(content).decode("utf-8")})
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
@@ -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
 
@@ -168,3 +168,11 @@ class ProsopoEnm(str, MyEnum):
168
168
 
169
169
  class CaptchaFoxEnm(str, MyEnum):
170
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 Union
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
- ) -> Union[dict, Exception]:
15
+ ) -> dict[str, str]:
16
16
  """
17
- Function periodically send the SYNC request to service and wait for captcha solving result
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
- captcha_response = GetTaskResultResponseSer(
25
- **requests.post(url_response, json=get_payload.to_dict()).json(), taskId=get_payload.taskId
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 captcha_response.status == "processing":
49
+ if response_ser.status == "processing":
30
50
  time.sleep(sleep_time)
31
51
  continue
32
- elif captcha_response.status == "ready":
52
+ elif response_ser.status == "ready":
33
53
  break
34
- elif captcha_response.errorId != 0:
35
- return captcha_response.to_dict()
54
+ elif response_ser.errorId != 0:
55
+ return response_ser.to_dict()
36
56
  except Exception as error:
37
- return error
38
- return captcha_response.to_dict()
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
- ) -> Union[dict, Exception]:
66
+ ) -> dict[str, str]:
44
67
  """
45
- Function periodically send the ASYNC request to service and wait for captcha solving result
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
- captcha_response = await resp.json(content_type=None)
57
- captcha_response = GetTaskResultResponseSer(**captcha_response, taskId=get_payload.taskId)
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 captcha_response.status == "processing":
104
+ if response_ser.status == "processing":
61
105
  await asyncio.sleep(sleep_time)
62
106
  continue
63
- elif captcha_response.status == "ready":
107
+ elif response_ser.status == "ready":
64
108
  break
65
- elif captcha_response.errorId != 0:
66
- return captcha_response.to_dict()
109
+ elif response_ser.errorId != 0:
110
+ return response_ser.to_dict()
67
111
  except Exception as error:
68
- return error
69
- return captcha_response.to_dict()
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 Literal, Optional
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
- return {f: getattr(self, f) for f in self.__struct_fields__}
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.value
59
+ service_type: enums.ServiceEnm | str = enums.ServiceEnm.TWOCAPTCHA
39
60
 
40
- url_request: Optional[str] = None
41
- url_response: Optional[str] = None
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 = None
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
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-rucaptcha
3
- Version: 6.4.0
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
6
  License-Expression: MIT
@@ -9,7 +9,7 @@ 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
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
13
13
  Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Programming Language :: Python
15
15
  Classifier: Programming Language :: Python :: 3
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.9
18
18
  Classifier: Programming Language :: Python :: 3.10
19
19
  Classifier: Programming Language :: Python :: 3.11
20
20
  Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
21
22
  Classifier: Framework :: AsyncIO
22
23
  Classifier: Operating System :: Unix
23
24
  Classifier: Operating System :: Microsoft :: Windows
@@ -1,5 +1,5 @@
1
1
  python_rucaptcha/__init__.py,sha256=odkQWtwx2RgIjD8pw2ZF-KR-5nL0Yu2Wn7Mu2YdBrSk,61
2
- python_rucaptcha/__version__.py,sha256=mLmu_37xnjX9cko1tT6SySyXdfQZ1f3ZP12S3GzhmVA,22
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
@@ -17,24 +17,26 @@ python_rucaptcha/fun_captcha.py,sha256=iU_twFRGKjeQPPBXfcqEK0d0kffNaCxPpHrihzBRg
17
17
  python_rucaptcha/gee_test.py,sha256=lxhqWm2uWWS_uQK5ufArjr-508pfecDYAEh4Ct-FthI,8583
18
18
  python_rucaptcha/grid_captcha.py,sha256=ssfl1BnBzxR1CHGdnjEG4wunTvEk9NvGIoiXQWHrt20,9001
19
19
  python_rucaptcha/hcaptcha.py,sha256=7i0S7A4LvBePUgVsCGpay8733Ve2rzjiZcR-CMTwa8s,3732
20
- python_rucaptcha/image_captcha.py,sha256=jWTO-kLSKXG12koO2X9dsFXCr3OjvXgj2aNJyhSomf4,8391
20
+ python_rucaptcha/image_captcha.py,sha256=mSLmqAbN2hO47FA-lm4q7PNakBKeEv1-Yt6jDF_q9-s,8448
21
21
  python_rucaptcha/key_captcha.py,sha256=hufzqYEb3ZR23AsLWIL82HFTJnA8JsOJtXUA92b9tws,4017
22
22
  python_rucaptcha/lemin_captcha.py,sha256=inEq-UWUbMX9aZdpyZBGRER-67YCS3PqxnDIpPAzmqM,4086
23
23
  python_rucaptcha/mt_captcha.py,sha256=usQK_LR9piDQM7lxkZbSKi6KIXedIBYyytKJBJe8jNg,3415
24
24
  python_rucaptcha/prosopo.py,sha256=kZLn_iZsm1mzQ8pNuPAzbfNfH8UBnLg1iy996Ti3nMo,3516
25
25
  python_rucaptcha/re_captcha.py,sha256=XyJncVbmaV5jZR-qxZ1JdFum10W5yk5zeVrDbZ5dSKo,6083
26
26
  python_rucaptcha/rotate_captcha.py,sha256=o9y9SVLPI6G6NqK1siv3xbOSYm7PGNquJbbhXuxTIDE,6278
27
+ python_rucaptcha/temu_captcha.py,sha256=Uny8OY-g4Nbl4_X8wZM8TsZzivG1r12AuPhYpdIM-fI,5830
27
28
  python_rucaptcha/tencent.py,sha256=f7lnRBUl-X5rL2FNRnrlh9ITRCcerTaS6XGUXnQdnOA,3649
28
29
  python_rucaptcha/text_captcha.py,sha256=lZDPwTgpMLvqvfjPbT-uNuNu6JRdWTEE9qfjnRQ5y-A,3708
29
30
  python_rucaptcha/turnstile.py,sha256=VpmYGYJoRVX6NHipO6-g2U5EpwxZToAZRmULj4ReDD0,3686
31
+ python_rucaptcha/vk_captcha.py,sha256=kpVQsyhd2eS9WqoK_30e30vOA3EBKrHcBwjCf4LHw1w,3837
30
32
  python_rucaptcha/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- python_rucaptcha/core/base.py,sha256=MvP0zQspHUmAgI8mZle_sYhs7H0ynUKwdhz5PwaIx4c,9425
32
- python_rucaptcha/core/config.py,sha256=UHP9iAaW3VpVC_7eHz_ihkYWIMAA9t_UXERfs5LwKiM,553
33
- python_rucaptcha/core/enums.py,sha256=QbEJlmbINkIoPr_-SoFtO3VhQ06lran_FABIjI-hZio,3987
34
- python_rucaptcha/core/result_handler.py,sha256=LJskNbfgsOdxwzXoNknzVPzCnxBrSR6EwsvOJgs0aD8,2724
35
- python_rucaptcha/core/serializer.py,sha256=asJRFD14uwOXG8gaNqsfaukiPBbsxt4r0rDA7lCuNHk,1739
36
- python_rucaptcha-6.4.0.dist-info/licenses/LICENSE,sha256=ZQeZG4IrO4YywdTal32gqyyrwdMlrMa3EwJCbgM8b2k,1063
37
- python_rucaptcha-6.4.0.dist-info/METADATA,sha256=y4PUMxNdSbKN2sDfBNl60B0sq5NQUkukUFej3OC9eF4,6572
38
- python_rucaptcha-6.4.0.dist-info/WHEEL,sha256=zaaOINJESkSfm_4HQVc5ssNzHCPXhJm0kEUakpsEHaU,91
39
- python_rucaptcha-6.4.0.dist-info/top_level.txt,sha256=Eu_atEB79Y7jCsfXPcXF5N8OLt6kKVbvhuRsI1BmSWM,17
40
- python_rucaptcha-6.4.0.dist-info/RECORD,,
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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.8.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5