playwright-captcha-solver 1.0.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.
- captcha_solver/__init__.py +36 -0
- captcha_solver/api_client.py +142 -0
- captcha_solver/captcha_reporter.py +18 -0
- captcha_solver/captcha_solver.py +67 -0
- captcha_solver/image_solver.py +127 -0
- captcha_solver/pipeline.py +106 -0
- captcha_solver/recaptcha_solver.py +161 -0
- captcha_solver/sdk_types.py +70 -0
- captcha_solver/submitter.py +65 -0
- playwright_captcha_solver-1.0.0.dist-info/METADATA +730 -0
- playwright_captcha_solver-1.0.0.dist-info/RECORD +14 -0
- playwright_captcha_solver-1.0.0.dist-info/WHEEL +5 -0
- playwright_captcha_solver-1.0.0.dist-info/licenses/LICENSE +38 -0
- playwright_captcha_solver-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from .captcha_solver import CaptchaSolver
|
|
2
|
+
|
|
3
|
+
from .sdk_types import (
|
|
4
|
+
AuthToken,
|
|
5
|
+
Credentials,
|
|
6
|
+
Authentication,
|
|
7
|
+
CaptchaSolverOptions,
|
|
8
|
+
ImageChallenge,
|
|
9
|
+
RecaptchaChallenge,
|
|
10
|
+
SolveResult,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .errors import (
|
|
14
|
+
CaptchaSolverError,
|
|
15
|
+
NetworkError,
|
|
16
|
+
TimeoutError,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"CaptchaSolver",
|
|
21
|
+
|
|
22
|
+
"AuthToken",
|
|
23
|
+
"Credentials",
|
|
24
|
+
"Authentication",
|
|
25
|
+
|
|
26
|
+
"CaptchaSolverOptions",
|
|
27
|
+
|
|
28
|
+
"ImageChallenge",
|
|
29
|
+
"RecaptchaChallenge",
|
|
30
|
+
|
|
31
|
+
"SolveResult",
|
|
32
|
+
|
|
33
|
+
"CaptchaSolverError",
|
|
34
|
+
"NetworkError",
|
|
35
|
+
"TimeoutError",
|
|
36
|
+
]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
|
|
7
|
+
from .sdk_types import (
|
|
8
|
+
AuthToken,
|
|
9
|
+
Credentials,
|
|
10
|
+
CaptchaSolverOptions,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
from .errors import (
|
|
14
|
+
CaptchaSolverError,
|
|
15
|
+
NetworkError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ApiClient:
|
|
20
|
+
def __init__(self, options: CaptchaSolverOptions):
|
|
21
|
+
self.options = options
|
|
22
|
+
|
|
23
|
+
self._client = httpx.AsyncClient(
|
|
24
|
+
follow_redirects=True,
|
|
25
|
+
timeout=options.timeout or 30,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
async def close(self) -> None:
|
|
29
|
+
await self._client.aclose()
|
|
30
|
+
|
|
31
|
+
async def request(
|
|
32
|
+
self,
|
|
33
|
+
endpoint: str,
|
|
34
|
+
*,
|
|
35
|
+
method: str = "GET",
|
|
36
|
+
body: Optional[dict] = None,
|
|
37
|
+
authenticated: bool = True,
|
|
38
|
+
files: Optional[dict] = None,
|
|
39
|
+
) -> httpx.Response:
|
|
40
|
+
|
|
41
|
+
params = None
|
|
42
|
+
data = body
|
|
43
|
+
|
|
44
|
+
if authenticated:
|
|
45
|
+
if files is not None:
|
|
46
|
+
data = self._append_authentication(body)
|
|
47
|
+
else:
|
|
48
|
+
params = self._authentication_query()
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
response = await self._client.request(
|
|
52
|
+
method=method,
|
|
53
|
+
url=endpoint,
|
|
54
|
+
params=params,
|
|
55
|
+
data=data,
|
|
56
|
+
files=files,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
except httpx.RequestError as exc:
|
|
60
|
+
raise NetworkError(
|
|
61
|
+
"Unable to reach the captcha service."
|
|
62
|
+
) from exc
|
|
63
|
+
|
|
64
|
+
self._handle_errors(response)
|
|
65
|
+
|
|
66
|
+
return response
|
|
67
|
+
|
|
68
|
+
def _authentication_query(self) -> dict:
|
|
69
|
+
|
|
70
|
+
auth = self.options.auth
|
|
71
|
+
|
|
72
|
+
if isinstance(auth, AuthToken):
|
|
73
|
+
return {
|
|
74
|
+
"authtoken": auth.authtoken,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if isinstance(auth, Credentials):
|
|
78
|
+
return {
|
|
79
|
+
"username": auth.username,
|
|
80
|
+
"password": auth.password,
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
raise TypeError("Unsupported authentication type.")
|
|
84
|
+
|
|
85
|
+
def _append_authentication(
|
|
86
|
+
self,
|
|
87
|
+
body: Optional[dict],
|
|
88
|
+
) -> dict:
|
|
89
|
+
|
|
90
|
+
body = dict(body or {})
|
|
91
|
+
|
|
92
|
+
auth = self.options.auth
|
|
93
|
+
|
|
94
|
+
if isinstance(auth, AuthToken):
|
|
95
|
+
body["authtoken"] = auth.authtoken
|
|
96
|
+
|
|
97
|
+
elif isinstance(auth, Credentials):
|
|
98
|
+
body["username"] = auth.username
|
|
99
|
+
body["password"] = auth.password
|
|
100
|
+
|
|
101
|
+
else:
|
|
102
|
+
raise TypeError("Unsupported authentication type.")
|
|
103
|
+
|
|
104
|
+
return body
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def _handle_errors(response: httpx.Response) -> None:
|
|
108
|
+
|
|
109
|
+
if response.is_success:
|
|
110
|
+
return
|
|
111
|
+
|
|
112
|
+
messages = {
|
|
113
|
+
255: "Not logged in. Check your credentials.",
|
|
114
|
+
|
|
115
|
+
400: (
|
|
116
|
+
"Captcha was rejected by the service. "
|
|
117
|
+
"Check that it is a valid image."
|
|
118
|
+
),
|
|
119
|
+
|
|
120
|
+
403: (
|
|
121
|
+
"Access denied. Check your credentials, "
|
|
122
|
+
"balance, or IP address."
|
|
123
|
+
),
|
|
124
|
+
|
|
125
|
+
413: (
|
|
126
|
+
"Captcha was rejected by the service. "
|
|
127
|
+
"Check that it is a valid image."
|
|
128
|
+
),
|
|
129
|
+
|
|
130
|
+
503: (
|
|
131
|
+
"Service overload. "
|
|
132
|
+
"Please try again later."
|
|
133
|
+
),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
raise CaptchaSolverError(
|
|
137
|
+
response.status_code,
|
|
138
|
+
messages.get(
|
|
139
|
+
response.status_code,
|
|
140
|
+
f"Unexpected HTTP status: {response.status_code}",
|
|
141
|
+
),
|
|
142
|
+
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from .api_client import ApiClient
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class CaptchaReporter:
|
|
5
|
+
def __init__(self, api: ApiClient):
|
|
6
|
+
self.api = api
|
|
7
|
+
|
|
8
|
+
async def report(
|
|
9
|
+
self,
|
|
10
|
+
captcha_id: str,
|
|
11
|
+
) -> None:
|
|
12
|
+
|
|
13
|
+
await self.api.request(
|
|
14
|
+
f"http://api.dbcapi.me/api/captcha/{captcha_id}/report",
|
|
15
|
+
method="POST",
|
|
16
|
+
body={},
|
|
17
|
+
authenticated=True,
|
|
18
|
+
)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from .api_client import ApiClient
|
|
4
|
+
from .pipeline import Pipeline
|
|
5
|
+
|
|
6
|
+
from .sdk_types import (
|
|
7
|
+
AuthToken,
|
|
8
|
+
Credentials,
|
|
9
|
+
CaptchaSolverOptions,
|
|
10
|
+
SolveResult,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CaptchaSolver:
|
|
15
|
+
def __init__(self, options: CaptchaSolverOptions):
|
|
16
|
+
|
|
17
|
+
self._validate_options(options)
|
|
18
|
+
|
|
19
|
+
self._api = ApiClient(options)
|
|
20
|
+
|
|
21
|
+
self._pipeline = Pipeline(self._api)
|
|
22
|
+
|
|
23
|
+
async def solve(self, options) -> SolveResult:
|
|
24
|
+
return await self._pipeline.solve(options)
|
|
25
|
+
|
|
26
|
+
async def report(self, captcha_id: str) -> None:
|
|
27
|
+
await self._pipeline.report_captcha(captcha_id)
|
|
28
|
+
|
|
29
|
+
async def close(self) -> None:
|
|
30
|
+
await self._api.close()
|
|
31
|
+
|
|
32
|
+
async def __aenter__(self):
|
|
33
|
+
return self
|
|
34
|
+
|
|
35
|
+
async def __aexit__(self, exc_type, exc, tb):
|
|
36
|
+
await self.close()
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def _validate_options(
|
|
40
|
+
options: CaptchaSolverOptions,
|
|
41
|
+
) -> None:
|
|
42
|
+
|
|
43
|
+
auth = options.auth
|
|
44
|
+
|
|
45
|
+
if isinstance(auth, AuthToken):
|
|
46
|
+
if not auth.authtoken:
|
|
47
|
+
raise ValueError(
|
|
48
|
+
"Authentication token is required."
|
|
49
|
+
)
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
if isinstance(auth, Credentials):
|
|
53
|
+
if not auth.username:
|
|
54
|
+
raise ValueError(
|
|
55
|
+
"Username is required."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if not auth.password:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
"Password is required."
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return
|
|
64
|
+
|
|
65
|
+
raise TypeError(
|
|
66
|
+
"Unsupported authentication type."
|
|
67
|
+
)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from urllib.parse import parse_qs
|
|
3
|
+
|
|
4
|
+
from playwright.async_api import Locator, Page
|
|
5
|
+
|
|
6
|
+
from .api_client import ApiClient
|
|
7
|
+
from .errors import TimeoutError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ImageSolver:
|
|
11
|
+
def __init__(self, api: ApiClient):
|
|
12
|
+
self.api = api
|
|
13
|
+
|
|
14
|
+
async def solve(
|
|
15
|
+
self,
|
|
16
|
+
page: Page,
|
|
17
|
+
captcha: Locator,
|
|
18
|
+
):
|
|
19
|
+
image = await captcha.screenshot(type="png")
|
|
20
|
+
|
|
21
|
+
files = {
|
|
22
|
+
"captchafile": (
|
|
23
|
+
"captcha.png",
|
|
24
|
+
image,
|
|
25
|
+
"image/png",
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
body = {
|
|
30
|
+
"type": "0",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
response = await self.api.request(
|
|
34
|
+
"http://api.dbcapi.me/api/captcha/",
|
|
35
|
+
method="POST",
|
|
36
|
+
body=body,
|
|
37
|
+
files=files,
|
|
38
|
+
authenticated=True,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
params = parse_qs(response.text)
|
|
42
|
+
|
|
43
|
+
captcha_id = params.get("captcha", [None])[0]
|
|
44
|
+
|
|
45
|
+
if captcha_id is None:
|
|
46
|
+
raise RuntimeError(
|
|
47
|
+
"Captcha service did not return a captcha ID."
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return await self.retrieve_token(captcha_id)
|
|
51
|
+
|
|
52
|
+
async def get_token(
|
|
53
|
+
self,
|
|
54
|
+
captcha_id: str,
|
|
55
|
+
) -> str:
|
|
56
|
+
|
|
57
|
+
response = await self.api.request(
|
|
58
|
+
f"http://api.dbcapi.me/api/captcha/{captcha_id}",
|
|
59
|
+
authenticated=False,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
return response.text
|
|
63
|
+
|
|
64
|
+
async def get_balance(self) -> float:
|
|
65
|
+
|
|
66
|
+
response = await self.api.request(
|
|
67
|
+
"http://api.dbcapi.me/api/user",
|
|
68
|
+
authenticated=True,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
params = parse_qs(response.text)
|
|
72
|
+
|
|
73
|
+
balance = params.get("balance", ["0"])[0]
|
|
74
|
+
|
|
75
|
+
return float(balance) / 100
|
|
76
|
+
|
|
77
|
+
async def retrieve_token(
|
|
78
|
+
self,
|
|
79
|
+
captcha_id: str,
|
|
80
|
+
):
|
|
81
|
+
|
|
82
|
+
print("Solving captcha...")
|
|
83
|
+
print("Captcha ID:", captcha_id)
|
|
84
|
+
|
|
85
|
+
max_attempts = 10
|
|
86
|
+
|
|
87
|
+
for attempt in range(max_attempts):
|
|
88
|
+
|
|
89
|
+
print(
|
|
90
|
+
f"Waiting 3 seconds before attempt {attempt + 1}..."
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
await asyncio.sleep(3)
|
|
94
|
+
|
|
95
|
+
print(
|
|
96
|
+
f"Checking result... Attempt {attempt + 1}"
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
result = await self.get_token(captcha_id)
|
|
100
|
+
|
|
101
|
+
params = parse_qs(result)
|
|
102
|
+
|
|
103
|
+
token = params.get("text", [None])[0]
|
|
104
|
+
|
|
105
|
+
if token and len(token) > 1:
|
|
106
|
+
|
|
107
|
+
balance = await self.get_balance()
|
|
108
|
+
|
|
109
|
+
print("Final response:")
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
"success": True,
|
|
113
|
+
"data": token,
|
|
114
|
+
"id": captcha_id,
|
|
115
|
+
"balance": balance,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if token == "?":
|
|
119
|
+
return {
|
|
120
|
+
"success": False,
|
|
121
|
+
"error": "Empty response.",
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
raise TimeoutError(
|
|
125
|
+
f"Captcha {captcha_id} was not solved "
|
|
126
|
+
f"after {max_attempts} attempts."
|
|
127
|
+
)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
from .api_client import ApiClient
|
|
4
|
+
from .captcha_reporter import CaptchaReporter
|
|
5
|
+
from .image_solver import ImageSolver
|
|
6
|
+
from .recaptcha_solver import RecaptchaSolver
|
|
7
|
+
from .submitter import Submitter
|
|
8
|
+
|
|
9
|
+
from .sdk_types import (
|
|
10
|
+
ImageChallenge,
|
|
11
|
+
RecaptchaChallenge,
|
|
12
|
+
SolveResult,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Pipeline:
|
|
17
|
+
def __init__(self, api: ApiClient):
|
|
18
|
+
|
|
19
|
+
self.submitter = Submitter()
|
|
20
|
+
|
|
21
|
+
self.image_solver = ImageSolver(api)
|
|
22
|
+
|
|
23
|
+
self.recaptcha_solver = RecaptchaSolver(api)
|
|
24
|
+
|
|
25
|
+
self.captcha_reporter = CaptchaReporter(api)
|
|
26
|
+
|
|
27
|
+
async def report_captcha(
|
|
28
|
+
self,
|
|
29
|
+
captcha_id: str,
|
|
30
|
+
) -> None:
|
|
31
|
+
|
|
32
|
+
await self.captcha_reporter.report(captcha_id)
|
|
33
|
+
|
|
34
|
+
async def solve(
|
|
35
|
+
self,
|
|
36
|
+
options,
|
|
37
|
+
) -> SolveResult:
|
|
38
|
+
|
|
39
|
+
started = time.perf_counter()
|
|
40
|
+
|
|
41
|
+
if isinstance(options, ImageChallenge):
|
|
42
|
+
|
|
43
|
+
solution = await self.image_solver.solve(
|
|
44
|
+
options.page,
|
|
45
|
+
options.captcha,
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if solution["success"]:
|
|
49
|
+
|
|
50
|
+
await self.submitter.submit_image(
|
|
51
|
+
options.page,
|
|
52
|
+
options.input,
|
|
53
|
+
solution["data"],
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
return SolveResult(
|
|
57
|
+
success=True,
|
|
58
|
+
challenge="image",
|
|
59
|
+
duration=int(
|
|
60
|
+
(time.perf_counter() - started) * 1000
|
|
61
|
+
),
|
|
62
|
+
id=solution["id"],
|
|
63
|
+
balance=solution["balance"],
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return SolveResult(
|
|
67
|
+
success=False,
|
|
68
|
+
challenge="image",
|
|
69
|
+
duration=int(
|
|
70
|
+
(time.perf_counter() - started) * 1000
|
|
71
|
+
),
|
|
72
|
+
id=None,
|
|
73
|
+
balance=None,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
response = await self.recaptcha_solver.solve(
|
|
77
|
+
options.page,
|
|
78
|
+
options.proxy,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if response["success"]:
|
|
82
|
+
|
|
83
|
+
await self.submitter.submit_recaptcha(
|
|
84
|
+
options.page,
|
|
85
|
+
response["data"],
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return SolveResult(
|
|
89
|
+
success=True,
|
|
90
|
+
challenge="recaptcha",
|
|
91
|
+
duration=int(
|
|
92
|
+
(time.perf_counter() - started) * 1000
|
|
93
|
+
),
|
|
94
|
+
id=response["id"],
|
|
95
|
+
balance=response["balance"],
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
return SolveResult(
|
|
99
|
+
success=False,
|
|
100
|
+
challenge="recaptcha",
|
|
101
|
+
duration=int(
|
|
102
|
+
(time.perf_counter() - started) * 1000
|
|
103
|
+
),
|
|
104
|
+
id=None,
|
|
105
|
+
balance=None,
|
|
106
|
+
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from urllib.parse import parse_qs, urlparse
|
|
3
|
+
|
|
4
|
+
from playwright.async_api import Page
|
|
5
|
+
|
|
6
|
+
from .api_client import ApiClient
|
|
7
|
+
from .errors import TimeoutError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RecaptchaSolver:
|
|
11
|
+
def __init__(self, api: ApiClient):
|
|
12
|
+
self.api = api
|
|
13
|
+
|
|
14
|
+
async def solve(
|
|
15
|
+
self,
|
|
16
|
+
page: Page,
|
|
17
|
+
proxy: str | None = None,
|
|
18
|
+
):
|
|
19
|
+
captcha = await self.get_captcha(page)
|
|
20
|
+
|
|
21
|
+
body = {
|
|
22
|
+
"type": "4",
|
|
23
|
+
"token_params": (
|
|
24
|
+
f'{{"pageurl":"{captcha["url"]}",'
|
|
25
|
+
f'"googlekey":"{captcha["sitekey"]}"'
|
|
26
|
+
+ (
|
|
27
|
+
f',"proxytype":"HTTP","proxy":"{proxy}"'
|
|
28
|
+
if proxy
|
|
29
|
+
else ""
|
|
30
|
+
)
|
|
31
|
+
+ "}"
|
|
32
|
+
),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
response = await self.api.request(
|
|
36
|
+
"http://api.dbcapi.me/api/captcha/",
|
|
37
|
+
method="POST",
|
|
38
|
+
body=body,
|
|
39
|
+
authenticated=True,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
params = parse_qs(response.text)
|
|
43
|
+
|
|
44
|
+
captcha_id = params.get("captcha", [None])[0]
|
|
45
|
+
|
|
46
|
+
if captcha_id is None:
|
|
47
|
+
raise RuntimeError(
|
|
48
|
+
"Captcha service did not return a captcha ID."
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return await self.retrieve_token(captcha_id)
|
|
52
|
+
|
|
53
|
+
async def get_captcha(
|
|
54
|
+
self,
|
|
55
|
+
page: Page,
|
|
56
|
+
) -> dict:
|
|
57
|
+
|
|
58
|
+
url = page.url
|
|
59
|
+
|
|
60
|
+
iframe = await page.locator(
|
|
61
|
+
'iframe[title="reCAPTCHA"]'
|
|
62
|
+
).get_attribute("src")
|
|
63
|
+
|
|
64
|
+
if iframe is None:
|
|
65
|
+
raise RuntimeError(
|
|
66
|
+
"No reCAPTCHA iframe found."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
parsed = urlparse(iframe)
|
|
70
|
+
|
|
71
|
+
params = parse_qs(parsed.query)
|
|
72
|
+
|
|
73
|
+
sitekey = params.get("k", [None])[0]
|
|
74
|
+
|
|
75
|
+
if sitekey is None:
|
|
76
|
+
raise RuntimeError(
|
|
77
|
+
"Unable to determine site key."
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
"url": url,
|
|
82
|
+
"sitekey": sitekey,
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async def get_token(
|
|
86
|
+
self,
|
|
87
|
+
captcha_id: str,
|
|
88
|
+
) -> str:
|
|
89
|
+
|
|
90
|
+
response = await self.api.request(
|
|
91
|
+
f"http://api.dbcapi.me/api/captcha/{captcha_id}",
|
|
92
|
+
authenticated=False,
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return response.text
|
|
96
|
+
|
|
97
|
+
async def get_balance(self) -> float:
|
|
98
|
+
|
|
99
|
+
response = await self.api.request(
|
|
100
|
+
"http://api.dbcapi.me/api/user",
|
|
101
|
+
authenticated=True,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
params = parse_qs(response.text)
|
|
105
|
+
|
|
106
|
+
balance = params.get("balance", ["0"])[0]
|
|
107
|
+
|
|
108
|
+
return float(balance) / 100
|
|
109
|
+
|
|
110
|
+
async def retrieve_token(
|
|
111
|
+
self,
|
|
112
|
+
captcha_id: str,
|
|
113
|
+
):
|
|
114
|
+
|
|
115
|
+
print("Solving reCAPTCHA...")
|
|
116
|
+
print("Captcha ID:", captcha_id)
|
|
117
|
+
|
|
118
|
+
max_attempts = 24
|
|
119
|
+
|
|
120
|
+
for attempt in range(max_attempts):
|
|
121
|
+
|
|
122
|
+
print(
|
|
123
|
+
f"Waiting 5 seconds before attempt {attempt + 1}..."
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
await asyncio.sleep(5)
|
|
127
|
+
|
|
128
|
+
print(
|
|
129
|
+
f"Checking result... Attempt {attempt + 1}"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
result = await self.get_token(captcha_id)
|
|
133
|
+
|
|
134
|
+
params = parse_qs(result)
|
|
135
|
+
|
|
136
|
+
token = params.get("text", [None])[0]
|
|
137
|
+
|
|
138
|
+
if token and len(token) > 1:
|
|
139
|
+
|
|
140
|
+
balance = await self.get_balance()
|
|
141
|
+
|
|
142
|
+
print("Final response:")
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
"success": True,
|
|
146
|
+
"data": token,
|
|
147
|
+
"id": captcha_id,
|
|
148
|
+
"balance": balance,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if token == "?":
|
|
152
|
+
return {
|
|
153
|
+
"success": False,
|
|
154
|
+
"error": "Empty response.",
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
raise TimeoutError(
|
|
158
|
+
f"Captcha {captcha_id} "
|
|
159
|
+
f"was not solved after "
|
|
160
|
+
f"{max_attempts} attempts."
|
|
161
|
+
)
|