Skip to content

Commit 36268ea

Browse files
committed
updated structure
1 parent 734e915 commit 36268ea

4 files changed

Lines changed: 109 additions & 166 deletions

File tree

src/python3_capsolver/control.py

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,33 @@
1-
from python3_capsolver.core.base import BaseCaptcha
2-
from python3_capsolver.core.enum import EndpointPostfixEnm
3-
from python3_capsolver.core.serializer import PostRequestSer, ControlResponseSer
1+
from python3_capsolver.core.serializer import PostRequestSer
42

3+
from .core.base import CaptchaParams
4+
from .core.const import GET_BALANCE_POSTFIX
5+
from .core.aio_captcha_instrument import AIOCaptchaInstrument
6+
from .core.sio_captcha_instrument import SIOCaptchaInstrument
57

6-
class Control(BaseCaptcha):
7-
"""
8-
The class is used to work with Capsolver control methods.
98

10-
Args:
11-
api_key: Capsolver API key
12-
13-
Notes:
14-
https://docs.capsolver.com/guide/api-getbalance.html
15-
"""
9+
class Control(CaptchaParams):
1610

1711
serializer = PostRequestSer
1812

1913
def __init__(
2014
self,
15+
api_key: str,
2116
*args,
2217
**kwargs,
2318
):
24-
super().__init__(*args, **kwargs)
19+
"""
20+
The class is used to work with Capsolver control methods.
21+
22+
Args:
23+
api_key: Capsolver API key
2524
26-
def get_balance(self) -> ControlResponseSer:
25+
Notes:
26+
https://docs.capsolver.com/guide/api-getbalance.html
27+
"""
28+
super().__init__(api_key=api_key, *args, **kwargs)
29+
30+
def get_balance(self) -> dict:
2731
"""
2832
Synchronous method to view the balance
2933
@@ -32,19 +36,19 @@ def get_balance(self) -> ControlResponseSer:
3236
ControlResponseSer(errorId=0 errorCode=None errorDescription=None balance=150.9085)
3337
3438
Returns:
35-
ResponseSer model with full server response
39+
Dict with full server response
3640
3741
Notes:
38-
https://docs.capsolver.com/guide/api-getbalance.html
42+
Check class docstring for more info
3943
"""
40-
self._prepare_create_task_payload(serializer=self.serializer)
41-
return ControlResponseSer(
42-
**self._create_task(
43-
url_postfix=EndpointPostfixEnm.GET_BALANCE.value,
44-
)
44+
self._captcha_handling_instrument = SIOCaptchaInstrument(captcha_params=self)
45+
return self._captcha_handling_instrument.send_post_request(
46+
session=self._captcha_handling_instrument.session,
47+
url_postfix=GET_BALANCE_POSTFIX,
48+
payload={"clientKey": self.create_task_payload.clientKey},
4549
)
4650

47-
async def aio_get_balance(self) -> ControlResponseSer:
51+
async def aio_get_balance(self) -> dict:
4852
"""
4953
Asynchronous method to view the balance
5054
@@ -53,14 +57,12 @@ async def aio_get_balance(self) -> ControlResponseSer:
5357
ControlResponseSer(errorId=0 errorCode=None errorDescription=None balance=150.9085)
5458
5559
Returns:
56-
ResponseSer model with full server response
60+
Dict with full server response
5761
5862
Notes:
59-
https://docs.capsolver.com/guide/api-getbalance.html
63+
Check class docstring for more info
6064
"""
61-
self._prepare_create_task_payload(serializer=self.serializer)
62-
return ControlResponseSer(
63-
**await self._aio_create_task(
64-
url_postfix=EndpointPostfixEnm.GET_BALANCE.value,
65-
)
65+
return await AIOCaptchaInstrument.send_post_request(
66+
url_postfix=GET_BALANCE_POSTFIX,
67+
payload={"clientKey": self.create_task_payload.clientKey},
6668
)

src/python3_capsolver/core/aio_captcha_instrument.py

Lines changed: 73 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@
33
import logging
44
from typing import Union, Optional
55
from urllib import parse
6-
from urllib.parse import urljoin
76

87
import aiohttp
98

10-
from .enum import SaveFormatsEnm
11-
from .const import ASYNC_RETRIES, BASE_REQUEST_URL, GET_RESULT_POSTFIX, CREATE_TASK_POSTFIX
9+
from .enum import SaveFormatsEnm, ResponseStatusEnm, EndpointPostfixEnm
10+
from .const import REQUEST_URL, ASYNC_RETRIES, VALID_STATUS_CODES, GET_BALANCE_POSTFIX
1211
from .utils import attempts_generator
13-
from .serializer import CreateTaskResponseSer
12+
from .serializer import CaptchaResponseSer
1413
from .captcha_instrument import CaptchaInstrument
1514

1615
__all__ = ("AIOCaptchaInstrument",)
@@ -24,21 +23,70 @@ class AIOCaptchaInstrument(CaptchaInstrument):
2423
def __init__(self, captcha_params: "CaptchaParams"):
2524
super().__init__()
2625
self.captcha_params = captcha_params
26+
self.created_task_data = CaptchaResponseSer
2727

2828
async def processing_captcha(self) -> dict:
29-
# added task params to payload
30-
self.captcha_params.create_task_payload.task.update(self.captcha_params.task_params)
29+
self.created_task_data = CaptchaResponseSer(**await self.__create_task())
3130

32-
created_task = await self._create_task()
31+
# if task created and already ready - return result
32+
if self.created_task_data.errorId == 0 and self.created_task_data.status == ResponseStatusEnm.Processing.value:
33+
return (await self.__get_result()).to_dict()
34+
return self.created_task_data.to_dict()
3335

34-
if created_task.errorId == 0:
35-
self.captcha_params.get_result_params.taskId = created_task.taskId
36-
else:
37-
return created_task.to_dict()
36+
async def __create_task(self, url_postfix: str = EndpointPostfixEnm.CREATE_TASK.value) -> dict:
37+
"""
38+
Function send the ASYNC request to service and wait for result
39+
"""
40+
async with aiohttp.ClientSession() as session:
41+
try:
42+
async with session.post(
43+
parse.urljoin(self.captcha_params.request_url, url_postfix),
44+
json=self.captcha_params.create_task_payload.to_dict(),
45+
) as resp:
46+
if resp.status in VALID_STATUS_CODES:
47+
return await resp.json()
48+
else:
49+
raise ValueError(resp.reason)
50+
except Exception as error:
51+
logging.exception(error)
52+
raise
3853

54+
async def __get_result(self, url_postfix: str = EndpointPostfixEnm.GET_TASK_RESULT.value) -> CaptchaResponseSer:
55+
"""
56+
Function send the ASYNC request to service and wait for result
57+
"""
58+
# initial waiting
3959
await asyncio.sleep(self.captcha_params.sleep_time)
4060

41-
return await self._get_result()
61+
self.captcha_params.get_result_params.taskId = self.created_task_data.taskId
62+
attempts = attempts_generator()
63+
async with aiohttp.ClientSession() as session:
64+
for _ in attempts:
65+
try:
66+
async with session.post(
67+
parse.urljoin(self.captcha_params.request_url, url_postfix),
68+
json=self.captcha_params.get_result_params.to_dict(),
69+
) as resp:
70+
if resp.status in VALID_STATUS_CODES:
71+
result_data = CaptchaResponseSer(**await resp.json())
72+
if result_data.status in (ResponseStatusEnm.Ready, ResponseStatusEnm.Failed):
73+
# if captcha ready\failed or have unknown status - return exist data
74+
return result_data
75+
else:
76+
raise ValueError(resp.reason)
77+
except Exception as error:
78+
logging.exception(error)
79+
raise
80+
81+
# if captcha just created or in processing now - wait
82+
await asyncio.sleep(self.captcha_params.sleep_time)
83+
84+
# default response if server is silent
85+
self.result.errorId = 1
86+
self.result.errorCode = self.CAPTCHA_UNSOLVABLE
87+
self.result.errorDescription = "Captcha not recognized"
88+
self.result.taskId = self.created_task_data.taskId
89+
self.result.status = ResponseStatusEnm.Failed
4290

4391
async def processing_image_captcha(
4492
self,
@@ -94,49 +142,22 @@ async def __body_file_processing(
94142
self.captcha_params.create_task_payload.task.update({"body": base64.b64encode(content).decode("utf-8")})
95143
except Exception as error:
96144
self.result.errorId = 12
97-
self.result.errorCode = self.NO_CAPTCHA_ERR
145+
self.result.errorCode = self.CAPTCHA_UNSOLVABLE
98146
self.result.errorDescription = str(error)
99147

100148
else:
101149
self.result.errorId = 12
102-
self.result.errorCode = self.NO_CAPTCHA_ERR
103-
104-
async def _url_read(self, url: str, **kwargs) -> bytes:
105-
"""
106-
Async method read bytes from link
107-
"""
108-
async with aiohttp.ClientSession() as session:
109-
async for attempt in ASYNC_RETRIES:
110-
with attempt:
111-
async with session.get(url=url, **kwargs) as resp:
112-
return await resp.content.read()
113-
114-
async def _create_task(self, url_postfix: str = CREATE_TASK_POSTFIX) -> CreateTaskResponseSer:
115-
"""
116-
Function send SYNC request to service and wait for result
117-
"""
118-
async with aiohttp.ClientSession() as session:
119-
try:
120-
async with session.post(
121-
parse.urljoin(BASE_REQUEST_URL, url_postfix), json=self.captcha_params.create_task_payload.to_dict()
122-
) as resp:
123-
if resp.status == 200:
124-
return CreateTaskResponseSer(**await resp.json())
125-
else:
126-
raise ValueError(resp.reason)
127-
except Exception as error:
128-
logging.exception(error)
129-
raise
150+
self.result.errorCode = self.CAPTCHA_UNSOLVABLE
130151

131152
@staticmethod
132-
async def send_post_request(payload: Optional[dict] = None, url_postfix: str = CREATE_TASK_POSTFIX) -> dict:
153+
async def send_post_request(payload: Optional[dict] = None, url_postfix: str = GET_BALANCE_POSTFIX) -> dict:
133154
"""
134155
Function send ASYNC request to service and wait for result
135156
"""
136157

137158
async with aiohttp.ClientSession() as session:
138159
try:
139-
async with session.post(parse.urljoin(BASE_REQUEST_URL, url_postfix), json=payload) as resp:
160+
async with session.post(parse.urljoin(REQUEST_URL, url_postfix), json=payload) as resp:
140161
if resp.status == 200:
141162
return await resp.json()
142163
else:
@@ -145,24 +166,13 @@ async def send_post_request(payload: Optional[dict] = None, url_postfix: str = C
145166
logging.exception(error)
146167
raise
147168

148-
async def _get_result(self, url_response: str = GET_RESULT_POSTFIX) -> dict:
149-
attempts = attempts_generator()
150-
# Send request for status of captcha solution.
169+
@staticmethod
170+
async def _url_read(url: str, **kwargs) -> bytes:
171+
"""
172+
Async method read bytes from link
173+
"""
151174
async with aiohttp.ClientSession() as session:
152-
for _ in attempts:
153-
async with session.post(
154-
url=urljoin(BASE_REQUEST_URL, url_response), json=self.captcha_params.get_result_params.to_dict()
155-
) as resp:
156-
json_result = await resp.json()
157-
# if there is no error, check CAPTCHA status
158-
if json_result["errorId"] == 0:
159-
# If not yet resolved, wait
160-
if json_result["status"] == "processing":
161-
await asyncio.sleep(self.captcha_params.sleep_time)
162-
# otherwise return response
163-
else:
164-
json_result.update({"taskId": self.captcha_params.get_result_params.taskId})
165-
return json_result
166-
else:
167-
json_result.update({"taskId": self.captcha_params.get_result_params.taskId})
168-
return json_result
175+
async for attempt in ASYNC_RETRIES:
176+
with attempt:
177+
async with session.get(url=url, **kwargs) as resp:
178+
return await resp.content.read()

src/python3_capsolver/core/base.py

Lines changed: 2 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
import asyncio
2-
import logging
3-
from urllib import parse
4-
5-
import aiohttp
6-
7-
from .enum import ResponseStatusEnm, EndpointPostfixEnm
8-
from .const import REQUEST_URL, VALID_STATUS_CODES
9-
from .utils import attempts_generator
10-
from .serializer import CaptchaResponseSer, RequestCreateTaskSer, RequestGetTaskResultSer
1+
from .const import REQUEST_URL
2+
from .serializer import RequestCreateTaskSer, RequestGetTaskResultSer
113
from .context_instr import AIOContextManager, SIOContextManager
124
from .captcha_instrument import CaptchaInstrument
135

@@ -44,67 +36,3 @@ def __init__(
4436
"""
4537
Async part
4638
"""
47-
48-
async def _aio_processing_captcha(self) -> dict:
49-
self._prepare_task_payload()
50-
self.created_task_data = CaptchaResponseSer(**await self._aio_create_task())
51-
52-
# if task created and already ready - return result
53-
if self.created_task_data.errorId == 0 and self.created_task_data.status == ResponseStatusEnm.Processing.value:
54-
return (await self._aio_get_result()).to_dict()
55-
return self.created_task_data.to_dict()
56-
57-
async def _aio_create_task(self, url_postfix: str = EndpointPostfixEnm.CREATE_TASK.value) -> dict:
58-
"""
59-
Function send the ASYNC request to service and wait for result
60-
"""
61-
async with aiohttp.ClientSession() as session:
62-
try:
63-
async with session.post(
64-
parse.urljoin(self.__request_url, url_postfix), json=self.create_task_payload.to_dict()
65-
) as resp:
66-
if resp.status in VALID_STATUS_CODES:
67-
return await resp.json()
68-
else:
69-
raise ValueError(resp.reason)
70-
except Exception as error:
71-
logging.exception(error)
72-
raise
73-
74-
async def _aio_get_result(self, url_postfix: str = EndpointPostfixEnm.GET_TASK_RESULT.value) -> CaptchaResponseSer:
75-
"""
76-
Function send the ASYNC request to service and wait for result
77-
"""
78-
# initial waiting
79-
await asyncio.sleep(self.__params.sleep_time)
80-
81-
self.get_result_params.taskId = self.created_task_data.taskId
82-
attempts = attempts_generator()
83-
async with aiohttp.ClientSession() as session:
84-
for _ in attempts:
85-
try:
86-
async with session.post(
87-
parse.urljoin(self.__request_url, url_postfix), json=self.get_result_params.to_dict()
88-
) as resp:
89-
if resp.status in VALID_STATUS_CODES:
90-
result_data = CaptchaResponseSer(**await resp.json())
91-
if result_data.status in (ResponseStatusEnm.Ready, ResponseStatusEnm.Failed):
92-
# if captcha ready\failed or have unknown status - return exist data
93-
return result_data
94-
else:
95-
raise ValueError(resp.reason)
96-
except Exception as error:
97-
logging.exception(error)
98-
raise
99-
100-
# if captcha just created or in processing now - wait
101-
await asyncio.sleep(self.__params.sleep_time)
102-
103-
# default response if server is silent
104-
return CaptchaResponseSer(
105-
errorId=1,
106-
errorCode="ERROR_CAPTCHA_UNSOLVABLE",
107-
errorDescription="Captcha not recognized",
108-
taskId=self.created_task_data.taskId,
109-
status=ResponseStatusEnm.Failed,
110-
)

src/python3_capsolver/core/const.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
ASYNC_RETRIES = AsyncRetrying(wait=wait_fixed(5), stop=stop_after_attempt(5), reraise=True)
99

1010
REQUEST_URL = "https://api.capsolver.com"
11+
CREATE_TASK_POSTFIX = "/createTask"
12+
GET_RESULT_POSTFIX = "/getTaskResult"
13+
GET_BALANCE_POSTFIX = "/getBalance"
1114
VALID_STATUS_CODES = (200, 202, 400, 401, 405)
1215

1316
APP_ID = "3E36E3CD-7EB5-4CAF-AA15-91011E652321"

0 commit comments

Comments
 (0)