hyper-sdk 0.1.2__tar.gz → 0.3.0__tar.gz
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.
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/PKG-INFO +97 -1
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/README.md +96 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/__init__.py +2 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/akamai/sec_cpt.py +6 -4
- hyper_sdk-0.3.0/hyper_sdk/akamai_input.py +33 -0
- hyper_sdk-0.3.0/hyper_sdk/datadome/__init__.py +1 -0
- hyper_sdk-0.3.0/hyper_sdk/datadome/parse.py +82 -0
- hyper_sdk-0.3.0/hyper_sdk/datadome_input.py +73 -0
- hyper_sdk-0.3.0/hyper_sdk/kasada/__init__.py +1 -0
- hyper_sdk-0.3.0/hyper_sdk/kasada/parse.py +27 -0
- hyper_sdk-0.3.0/hyper_sdk/kasada_input.py +37 -0
- hyper_sdk-0.3.0/hyper_sdk/session.py +265 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk.egg-info/PKG-INFO +97 -1
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk.egg-info/SOURCES.txt +7 -1
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/pyproject.toml +2 -2
- hyper_sdk-0.1.2/hyper_sdk/akamai_input.py +0 -15
- hyper_sdk-0.1.2/hyper_sdk/session.py +0 -138
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/LICENSE +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/akamai/__init__.py +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/akamai/pixel.py +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/akamai/script_path.py +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/akamai/stop_signal.py +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/incapsula/__init__.py +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/incapsula/utmvc.py +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk/incapsula_input.py +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk.egg-info/dependency_links.txt +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk.egg-info/requires.txt +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/hyper_sdk.egg-info/top_level.txt +0 -0
- {hyper_sdk-0.1.2 → hyper_sdk-0.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hyper_sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Hyper Solutions Python SDK
|
|
5
5
|
Project-URL: homepage, https://github.com/Hyper-Solutions/hyper-sdk-py
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -192,3 +192,99 @@ from hyper_sdk.incapsula.utmvc import get_utmvc_submit_path
|
|
|
192
192
|
|
|
193
193
|
submit_path = get_utmvc_submit_path()
|
|
194
194
|
```
|
|
195
|
+
|
|
196
|
+
## Kasada
|
|
197
|
+
|
|
198
|
+
The Kasada package provides functions for interacting with Kasada Bot Manager, including parsing script path.
|
|
199
|
+
|
|
200
|
+
### Generating Payload Data (CT)
|
|
201
|
+
|
|
202
|
+
To generate payload data required for generating valid `x-kpsdk-ct` tokens, use the `generate_kasada_payload` function:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
payload, headers = hyper_session.generate_kasada_payload(hyper_sdk.KasadaPayloadInput(
|
|
206
|
+
user_agent=USER_AGENT,
|
|
207
|
+
ips_link=ips_link,
|
|
208
|
+
script=ips_script,
|
|
209
|
+
language="en-US"
|
|
210
|
+
))
|
|
211
|
+
# Use payload and headers for the next request
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Generating Pow Data (CD)
|
|
215
|
+
|
|
216
|
+
To generate POW data (`x-kpsdk-cd`) tokens, use the `generate_kasada_pow` function:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
cd = hyper_session.generate_kasada_pow(hyper_sdk.KasadaPowInput(
|
|
220
|
+
st=st,
|
|
221
|
+
work_time=None
|
|
222
|
+
))
|
|
223
|
+
# Use cd as the x-kpsdk-cd header in the next request
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Parsing Script Path
|
|
227
|
+
|
|
228
|
+
To parse the Kasada script path from the given blocked page (status code 429) HTML code, use the `parse_script_path` function:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from hyper_sdk.kasada import parse_script_path
|
|
232
|
+
|
|
233
|
+
script_path = parse_script_path(html_content)
|
|
234
|
+
# script_path will look like: /ips.js?...
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## DataDome
|
|
238
|
+
|
|
239
|
+
The DataDome package provides functions for interacting with DataDome Bot Manager, including parsing device link URLs
|
|
240
|
+
for interstitial and slider challenges.
|
|
241
|
+
|
|
242
|
+
### Generating Interstitial Payload
|
|
243
|
+
|
|
244
|
+
To generate payload data required for solving interstitial challenges, use the `generate_interstitial_payload` function:
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
payload = hyper_session.generate_interstitial_payload(hyper_sdk.DataDomeInterstitialInput(
|
|
248
|
+
user_agent=USER_AGENT,
|
|
249
|
+
device_link=device_check_link,
|
|
250
|
+
html=html_content
|
|
251
|
+
))
|
|
252
|
+
# Use the payload to POST to https://geo.captcha-delivery.com/interstitial/
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Generating Slider Payload
|
|
256
|
+
|
|
257
|
+
To solve DataDome Slider challenges, use the `generate_slider_payload` function:
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
payload = hyper_session.generate_slider_payload(hyper_sdk.DataDomeSliderInput(
|
|
261
|
+
user_agent=USER_AGENT,
|
|
262
|
+
device_link=device_check_link,
|
|
263
|
+
html=html_content,
|
|
264
|
+
puzzle=base64_encoded_puzzle,
|
|
265
|
+
piece=base64_encoded_piece
|
|
266
|
+
))
|
|
267
|
+
# Create a GET request to the payload URL
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Parsing Interstitial DeviceLink URL
|
|
271
|
+
|
|
272
|
+
To parse the Interstitial DeviceLink URL from the HTML code, use the `parse_interstitial_device_check_link` function:
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
from hyper_sdk.datadome import parse_interstitial_device_check_link
|
|
276
|
+
|
|
277
|
+
device_link = parse_interstitial_device_check_link(html_content, datadome_cookie, referer)
|
|
278
|
+
# device_link will look like: https://geo.captcha-delivery.com/interstitial/?...
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Parsing Slider DeviceLink URL
|
|
282
|
+
|
|
283
|
+
To parse the Slider DeviceLink URL from the HTML code, use the `parse_slider_device_check_link` function:
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from hyper_sdk.datadome import parse_slider_device_check_link
|
|
287
|
+
|
|
288
|
+
device_link = parse_slider_device_check_link(html_content, datadome_cookie, referer)
|
|
289
|
+
# device_link will look like: https://geo.captcha-delivery.com/captcha/?...
|
|
290
|
+
```
|
|
@@ -172,4 +172,100 @@ To generate a unique UTMVC submit path with a random query parameter, use the `g
|
|
|
172
172
|
from hyper_sdk.incapsula.utmvc import get_utmvc_submit_path
|
|
173
173
|
|
|
174
174
|
submit_path = get_utmvc_submit_path()
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Kasada
|
|
178
|
+
|
|
179
|
+
The Kasada package provides functions for interacting with Kasada Bot Manager, including parsing script path.
|
|
180
|
+
|
|
181
|
+
### Generating Payload Data (CT)
|
|
182
|
+
|
|
183
|
+
To generate payload data required for generating valid `x-kpsdk-ct` tokens, use the `generate_kasada_payload` function:
|
|
184
|
+
|
|
185
|
+
```python
|
|
186
|
+
payload, headers = hyper_session.generate_kasada_payload(hyper_sdk.KasadaPayloadInput(
|
|
187
|
+
user_agent=USER_AGENT,
|
|
188
|
+
ips_link=ips_link,
|
|
189
|
+
script=ips_script,
|
|
190
|
+
language="en-US"
|
|
191
|
+
))
|
|
192
|
+
# Use payload and headers for the next request
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Generating Pow Data (CD)
|
|
196
|
+
|
|
197
|
+
To generate POW data (`x-kpsdk-cd`) tokens, use the `generate_kasada_pow` function:
|
|
198
|
+
|
|
199
|
+
```python
|
|
200
|
+
cd = hyper_session.generate_kasada_pow(hyper_sdk.KasadaPowInput(
|
|
201
|
+
st=st,
|
|
202
|
+
work_time=None
|
|
203
|
+
))
|
|
204
|
+
# Use cd as the x-kpsdk-cd header in the next request
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Parsing Script Path
|
|
208
|
+
|
|
209
|
+
To parse the Kasada script path from the given blocked page (status code 429) HTML code, use the `parse_script_path` function:
|
|
210
|
+
|
|
211
|
+
```python
|
|
212
|
+
from hyper_sdk.kasada import parse_script_path
|
|
213
|
+
|
|
214
|
+
script_path = parse_script_path(html_content)
|
|
215
|
+
# script_path will look like: /ips.js?...
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## DataDome
|
|
219
|
+
|
|
220
|
+
The DataDome package provides functions for interacting with DataDome Bot Manager, including parsing device link URLs
|
|
221
|
+
for interstitial and slider challenges.
|
|
222
|
+
|
|
223
|
+
### Generating Interstitial Payload
|
|
224
|
+
|
|
225
|
+
To generate payload data required for solving interstitial challenges, use the `generate_interstitial_payload` function:
|
|
226
|
+
|
|
227
|
+
```python
|
|
228
|
+
payload = hyper_session.generate_interstitial_payload(hyper_sdk.DataDomeInterstitialInput(
|
|
229
|
+
user_agent=USER_AGENT,
|
|
230
|
+
device_link=device_check_link,
|
|
231
|
+
html=html_content
|
|
232
|
+
))
|
|
233
|
+
# Use the payload to POST to https://geo.captcha-delivery.com/interstitial/
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Generating Slider Payload
|
|
237
|
+
|
|
238
|
+
To solve DataDome Slider challenges, use the `generate_slider_payload` function:
|
|
239
|
+
|
|
240
|
+
```python
|
|
241
|
+
payload = hyper_session.generate_slider_payload(hyper_sdk.DataDomeSliderInput(
|
|
242
|
+
user_agent=USER_AGENT,
|
|
243
|
+
device_link=device_check_link,
|
|
244
|
+
html=html_content,
|
|
245
|
+
puzzle=base64_encoded_puzzle,
|
|
246
|
+
piece=base64_encoded_piece
|
|
247
|
+
))
|
|
248
|
+
# Create a GET request to the payload URL
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Parsing Interstitial DeviceLink URL
|
|
252
|
+
|
|
253
|
+
To parse the Interstitial DeviceLink URL from the HTML code, use the `parse_interstitial_device_check_link` function:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from hyper_sdk.datadome import parse_interstitial_device_check_link
|
|
257
|
+
|
|
258
|
+
device_link = parse_interstitial_device_check_link(html_content, datadome_cookie, referer)
|
|
259
|
+
# device_link will look like: https://geo.captcha-delivery.com/interstitial/?...
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Parsing Slider DeviceLink URL
|
|
263
|
+
|
|
264
|
+
To parse the Slider DeviceLink URL from the HTML code, use the `parse_slider_device_check_link` function:
|
|
265
|
+
|
|
266
|
+
```python
|
|
267
|
+
from hyper_sdk.datadome import parse_slider_device_check_link
|
|
268
|
+
|
|
269
|
+
device_link = parse_slider_device_check_link(html_content, datadome_cookie, referer)
|
|
270
|
+
# device_link will look like: https://geo.captcha-delivery.com/captcha/?...
|
|
175
271
|
```
|
|
@@ -9,15 +9,16 @@ from typing import List
|
|
|
9
9
|
|
|
10
10
|
sec_duration_expr = re.compile(r'data-duration=(\d+)')
|
|
11
11
|
sec_challenge_expr = re.compile(r'challenge="(.*?)"')
|
|
12
|
-
sec_page_expr = re.compile(r'src="(
|
|
12
|
+
sec_page_expr = re.compile(r'data-duration=\d+\s+src="([^"]+)"')
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
class SecCptChallengeData:
|
|
16
|
-
def __init__(self, token: str, timestamp: int, nonce: str, difficulty: int):
|
|
16
|
+
def __init__(self, token: str, timestamp: int, nonce: str, difficulty: int, count: int):
|
|
17
17
|
self.token = token
|
|
18
18
|
self.timestamp = timestamp
|
|
19
19
|
self.nonce = nonce
|
|
20
20
|
self.difficulty = difficulty
|
|
21
|
+
self.count = count
|
|
21
22
|
|
|
22
23
|
|
|
23
24
|
class SecCptChallenge:
|
|
@@ -63,7 +64,8 @@ class SecCptChallenge:
|
|
|
63
64
|
challenge_data.get('token', ''),
|
|
64
65
|
challenge_data.get('timestamp', 0),
|
|
65
66
|
challenge_data.get('nonce', ''),
|
|
66
|
-
challenge_data.get('difficulty', 0)
|
|
67
|
+
challenge_data.get('difficulty', 0),
|
|
68
|
+
challenge_data.get('count', 0)
|
|
67
69
|
)
|
|
68
70
|
|
|
69
71
|
@staticmethod
|
|
@@ -121,7 +123,7 @@ class SecCptChallenge:
|
|
|
121
123
|
difficulty += 1
|
|
122
124
|
answers.append(answer)
|
|
123
125
|
|
|
124
|
-
if len(answers) ==
|
|
126
|
+
if len(answers) == self.challenge_data.count:
|
|
125
127
|
break
|
|
126
128
|
continue
|
|
127
129
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class SensorInput:
|
|
2
|
+
def __init__(self, abck: str, bmsz: str, version: str, page_url: str, user_agent: str, ip = "", language = "", script_hash="", dynamic_values=""):
|
|
3
|
+
self.abck = abck
|
|
4
|
+
self.bmsz = bmsz
|
|
5
|
+
self.version = version
|
|
6
|
+
self.page_url = page_url
|
|
7
|
+
self.user_agent = user_agent
|
|
8
|
+
self.script_hash = script_hash
|
|
9
|
+
self.dynamic_values = dynamic_values
|
|
10
|
+
self.ip = ip
|
|
11
|
+
self.language = language
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PixelInput:
|
|
15
|
+
def __init__(self, user_agent: str, html_var: str, script_var: str):
|
|
16
|
+
self.user_agent = user_agent
|
|
17
|
+
self.html_var = html_var
|
|
18
|
+
self.script_var = script_var
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DynamicInput:
|
|
22
|
+
def __init__(self, script: str):
|
|
23
|
+
self.script = script
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SbsdInput:
|
|
27
|
+
def __init__(self, user_agent: str, uuid: str, page_url: str, o_cookie: str, language = "", ip = ""):
|
|
28
|
+
self.user_agent = user_agent
|
|
29
|
+
self.uuid = uuid
|
|
30
|
+
self.page_url = page_url
|
|
31
|
+
self.o_cookie = o_cookie
|
|
32
|
+
self.language = language
|
|
33
|
+
self.ip = ip
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .parse import *
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from urllib.parse import urlencode
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def parse_slider_device_check_link(src: str, datadome_cookie: str, referer: str) -> str:
|
|
6
|
+
"""
|
|
7
|
+
Parse the device check URL for DataDome slider captcha from a blocked response body.
|
|
8
|
+
|
|
9
|
+
This function extracts the necessary parameters from the DataDome JavaScript object
|
|
10
|
+
embedded in the HTML source and constructs the URL for the slider captcha challenge.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
src (str): The HTML source of the blocked page containing the DataDome JavaScript object.
|
|
14
|
+
datadome_cookie (str): The current value of the 'datadome' cookie.
|
|
15
|
+
referer (str): The referer URL to be included in the device check link.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: The constructed device check URL for the slider captcha.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
RuntimeError: If the dd object cannot be extracted or parsed,
|
|
22
|
+
or if the proxy is blocked (indicated by 't' == 'bv').
|
|
23
|
+
"""
|
|
24
|
+
try:
|
|
25
|
+
dd_object = src.split("var dd=")[1].split("</script>")[0]
|
|
26
|
+
dd_object = dd_object.replace("'", '"')
|
|
27
|
+
dd_object_parsed = json.loads(dd_object)
|
|
28
|
+
except Exception as _:
|
|
29
|
+
raise RuntimeError("Failed to parse dd object.")
|
|
30
|
+
|
|
31
|
+
if dd_object_parsed.get("t") == "bv":
|
|
32
|
+
raise RuntimeError("proxy blocked")
|
|
33
|
+
|
|
34
|
+
params = {
|
|
35
|
+
"initialCid": dd_object_parsed.get("cid"),
|
|
36
|
+
"hash": dd_object_parsed.get("hsh"),
|
|
37
|
+
"cid": datadome_cookie,
|
|
38
|
+
"t": dd_object_parsed.get("t"),
|
|
39
|
+
"referer": referer,
|
|
40
|
+
"s": str(dd_object_parsed.get("s")),
|
|
41
|
+
"e": dd_object_parsed.get("e"),
|
|
42
|
+
"dm": "cd",
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return f"https://geo.captcha-delivery.com/captcha/?{urlencode(params)}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_interstitial_device_check_link(src: str, datadome_cookie: str, referer: str) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Parse the device check URL for DataDome interstitial challenge from a blocked response body.
|
|
51
|
+
|
|
52
|
+
This function extracts the necessary parameters from the DataDome JavaScript object
|
|
53
|
+
embedded in the HTML source and constructs the URL for the interstitial challenge.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
src (str): The HTML source of the blocked page containing the DataDome JavaScript object.
|
|
57
|
+
datadome_cookie (str): The current value of the 'datadome' cookie.
|
|
58
|
+
referer (str): The referer URL to be included in the device check link.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
str: The constructed device check URL for the interstitial challenge.
|
|
62
|
+
|
|
63
|
+
Raises:
|
|
64
|
+
RuntimeError: If the DataDome dd object cannot be extracted or parsed.
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
dd_object = src.split("var dd=")[1].split("</script>")[0]
|
|
68
|
+
dd_object = dd_object.replace("'", '"')
|
|
69
|
+
dd_object_parsed = json.loads(dd_object)
|
|
70
|
+
except Exception as _:
|
|
71
|
+
raise RuntimeError("Failed to parse dd object.")
|
|
72
|
+
|
|
73
|
+
params = {
|
|
74
|
+
"initialCid": dd_object_parsed.get("cid"),
|
|
75
|
+
"hash": dd_object_parsed.get("hsh"),
|
|
76
|
+
"cid": datadome_cookie,
|
|
77
|
+
"referer": referer,
|
|
78
|
+
"s": str(dd_object_parsed.get("s")),
|
|
79
|
+
"b": str(dd_object_parsed.get("b")),
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return f"https://geo.captcha-delivery.com/interstitial/?{urlencode(params)}"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
class DataDomeSliderInput:
|
|
2
|
+
def __init__(self, user_agent: str, device_link: str, html: str, puzzle: str, piece: str):
|
|
3
|
+
# UserAgent must be a Chrome Windows User-Agent.
|
|
4
|
+
self.user_agent = user_agent
|
|
5
|
+
|
|
6
|
+
# DeviceLink is the URL that contains the script and starts like this:
|
|
7
|
+
# https://geo.captcha-delivery.com/captcha/?initialCid
|
|
8
|
+
self.device_link = device_link
|
|
9
|
+
|
|
10
|
+
# Html is the response body of the GET request to the DeviceLink
|
|
11
|
+
self.html = html
|
|
12
|
+
|
|
13
|
+
# Puzzle is the captcha puzzle image bytes, base64 encoded.
|
|
14
|
+
# The URL that returns the puzzle looks like this:
|
|
15
|
+
# https://dd.prod.captcha-delivery.com/image/2024-xx-xx/hash.jpg
|
|
16
|
+
self.puzzle = puzzle
|
|
17
|
+
|
|
18
|
+
# Piece is the captcha puzzle piece image bytes, base64 encoded.
|
|
19
|
+
# The URL that returns the puzzle looks like this:
|
|
20
|
+
# https://dd.prod.captcha-delivery.com/image/2024-xx-xx/hash.frag.png
|
|
21
|
+
self.piece = piece
|
|
22
|
+
|
|
23
|
+
def to_dict(self):
|
|
24
|
+
return {
|
|
25
|
+
"userAgent": self.user_agent,
|
|
26
|
+
"deviceLink": self.device_link,
|
|
27
|
+
"html": self.html,
|
|
28
|
+
"puzzle": self.puzzle,
|
|
29
|
+
"piece": self.piece
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class DataDomeInterstitialInput:
|
|
34
|
+
def __init__(self, user_agent: str, device_link: str, html: str):
|
|
35
|
+
# UserAgent must be a Chrome Windows User-Agent.
|
|
36
|
+
self.user_agent = user_agent
|
|
37
|
+
|
|
38
|
+
# DeviceLink is the URL that contains the script and starts like this:
|
|
39
|
+
# https://geo.captcha-delivery.com/captcha/?initialCid
|
|
40
|
+
self.device_link = device_link
|
|
41
|
+
|
|
42
|
+
# Html is the response body of the GET request to the DeviceLink
|
|
43
|
+
self.html = html
|
|
44
|
+
|
|
45
|
+
def to_dict(self):
|
|
46
|
+
return {
|
|
47
|
+
"userAgent": self.user_agent,
|
|
48
|
+
"deviceLink": self.device_link,
|
|
49
|
+
"html": self.html
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class DataDomeTagsInput:
|
|
54
|
+
def __init__(self, user_agent: str, cid: str, ddk: str, referer: str, tags_type: str, language = "", ip = ""):
|
|
55
|
+
# UserAgent must be a Chrome Windows User-Agent.
|
|
56
|
+
self.user_agent = user_agent
|
|
57
|
+
self.cid = cid
|
|
58
|
+
self.ddk = ddk
|
|
59
|
+
self.referer = referer
|
|
60
|
+
self.tags_type = tags_type
|
|
61
|
+
self.language = language
|
|
62
|
+
self.ip = ip
|
|
63
|
+
|
|
64
|
+
def to_dict(self):
|
|
65
|
+
return {
|
|
66
|
+
"userAgent": self.user_agent,
|
|
67
|
+
"cid": self.cid,
|
|
68
|
+
"ddk": self.ddk,
|
|
69
|
+
"referer": self.referer,
|
|
70
|
+
"type": self.tags_type,
|
|
71
|
+
"language": self.language,
|
|
72
|
+
"ip": self.ip,
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .parse import *
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import re
|
|
2
|
+
|
|
3
|
+
# Precompiled regular expression
|
|
4
|
+
script_path_expr = re.compile(r'<script\s+src="([^"]+)"')
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def parse_script_path(src: str) -> str:
|
|
8
|
+
"""
|
|
9
|
+
Gets the Akamai Bot Manager web SDK path from the given HTML code src.
|
|
10
|
+
|
|
11
|
+
This function searches the provided HTML source code for the path of a JavaScript script tag that matches the
|
|
12
|
+
specified regular expression pattern.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
src (str): The HTML source code as a string.
|
|
16
|
+
|
|
17
|
+
Returns:
|
|
18
|
+
str: The path of the script extracted from the script tag.
|
|
19
|
+
|
|
20
|
+
Raises:
|
|
21
|
+
Exception: If the script path is not found in the source.
|
|
22
|
+
"""
|
|
23
|
+
match = script_path_expr.search(src)
|
|
24
|
+
if match:
|
|
25
|
+
return re.sub(r'&', '&', match.group(1))
|
|
26
|
+
else:
|
|
27
|
+
raise Exception("hyper-sdk: script path not found")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class KasadaPowInput:
|
|
2
|
+
def __init__(self, st: int, work_time: int = None):
|
|
3
|
+
# St is the x-kpsdk-st value returned by the /tl POST request
|
|
4
|
+
self.st = st
|
|
5
|
+
# WorkTime can be used to pre-generate POW strings
|
|
6
|
+
self.work_time = work_time
|
|
7
|
+
|
|
8
|
+
def to_dict(self):
|
|
9
|
+
result = {"st": self.st}
|
|
10
|
+
if self.work_time is not None:
|
|
11
|
+
result["workTime"] = self.work_time
|
|
12
|
+
return result
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class KasadaPayloadInput:
|
|
16
|
+
def __init__(self, user_agent: str, ips_link: str, script: str, language: str = "en-US"):
|
|
17
|
+
# UserAgent must be a Chrome Windows User-Agent.
|
|
18
|
+
self.user_agent = user_agent
|
|
19
|
+
|
|
20
|
+
# IpsLink is the ips.js script link, parsed from the block page (429 status code)
|
|
21
|
+
self.ips_link = ips_link
|
|
22
|
+
|
|
23
|
+
# Script is the ips.js script retrieved using the IpsLink url
|
|
24
|
+
self.script = script
|
|
25
|
+
|
|
26
|
+
# Language is the first language of your accept-language header, it defaults to "en-US" if left empty.
|
|
27
|
+
self.language = language
|
|
28
|
+
|
|
29
|
+
def to_dict(self):
|
|
30
|
+
result = {
|
|
31
|
+
"userAgent": self.user_agent,
|
|
32
|
+
"ipsLink": self.ips_link,
|
|
33
|
+
"script": self.script
|
|
34
|
+
}
|
|
35
|
+
if self.language != "en-US":
|
|
36
|
+
result["language"] = self.language
|
|
37
|
+
return result
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
from typing import Optional, Dict, Any
|
|
2
|
+
from urllib.parse import quote
|
|
3
|
+
|
|
4
|
+
import requests
|
|
5
|
+
import jwt
|
|
6
|
+
from datetime import datetime, timedelta, timezone
|
|
7
|
+
|
|
8
|
+
from .akamai_input import SensorInput, PixelInput, DynamicInput, SbsdInput
|
|
9
|
+
from .kasada_input import KasadaPowInput, KasadaPayloadInput
|
|
10
|
+
from .datadome_input import DataDomeSliderInput, DataDomeInterstitialInput, DataDomeTagsInput
|
|
11
|
+
from .incapsula_input import UtmvcInput
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Session:
|
|
15
|
+
def __init__(self, api_key: str, jwt_key: Optional[str] = None, client: Optional[requests.Session] = None) -> None:
|
|
16
|
+
self.api_key = api_key
|
|
17
|
+
self.jwt_key = jwt_key
|
|
18
|
+
self.client = requests.Session() if client is None else client
|
|
19
|
+
|
|
20
|
+
def generate_sensor_data(self, input_data: SensorInput) -> str:
|
|
21
|
+
"""
|
|
22
|
+
Returns the sensor data required to generate valid akamai cookies using the Hyper Solutions API.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
input_data (SensorInput): An instance of SensorInput containing the necessary data for generating the sensor data.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
str: Sensor data as a string.
|
|
29
|
+
"""
|
|
30
|
+
sensor_endpoint = "https://akm.justhyped.dev/sensor"
|
|
31
|
+
return self._send_request(sensor_endpoint, {
|
|
32
|
+
'userAgent': input_data.user_agent,
|
|
33
|
+
'abck': input_data.abck,
|
|
34
|
+
'bmsz': input_data.bmsz,
|
|
35
|
+
'version': input_data.version,
|
|
36
|
+
'pageUrl': input_data.page_url,
|
|
37
|
+
'scriptHash': input_data.script_hash,
|
|
38
|
+
'dynamicValues': input_data.dynamic_values,
|
|
39
|
+
'ip': input_data.ip,
|
|
40
|
+
'language': input_data.language,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
def generate_sbsd_data(self, input_data: SbsdInput) -> str:
|
|
44
|
+
"""
|
|
45
|
+
Returns the sbsd data required to solve SBSD using the Hyper Solutions API.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
input_data (SbsdInput): An instance of SbsdInput containing the necessary data for generating the sbsd data.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
str: Sensor data as a string.
|
|
52
|
+
"""
|
|
53
|
+
sensor_endpoint = "https://akm.justhyped.dev/sbsd"
|
|
54
|
+
return self._send_request(sensor_endpoint, {
|
|
55
|
+
'userAgent': input_data.user_agent,
|
|
56
|
+
'uuid': input_data.uuid,
|
|
57
|
+
'pageUrl': input_data.page_url,
|
|
58
|
+
'o': input_data.o_cookie,
|
|
59
|
+
'language': input_data.language,
|
|
60
|
+
'ip': input_data.ip,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
def parse_v3_dynamic(self, input_data: DynamicInput) -> str:
|
|
64
|
+
"""
|
|
65
|
+
Returns the dynamic values required to generate sensor data for V3 dynamic with Hyper Solutions API.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
input_data (DynamicInput): An instance of DynamicInput containing the necessary data for parsing the script.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
str: Dynamic values as a string.
|
|
72
|
+
"""
|
|
73
|
+
sensor_endpoint = "https://akm.justhyped.dev/dynamic"
|
|
74
|
+
return self._send_request(sensor_endpoint, {
|
|
75
|
+
'script': input_data.script,
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
def generate_pixel_data(self, input_data: PixelInput) -> str:
|
|
79
|
+
"""
|
|
80
|
+
Returns the pixel data using the Hyper Solutions API.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
session (Session): An instance of Session to handle the network request.
|
|
84
|
+
input_data (PixelInput): An instance of PixelInput containing the necessary data for generating the pixel data.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
str: Pixel data as a string.
|
|
88
|
+
"""
|
|
89
|
+
pixel_endpoint = "https://akm.justhyped.dev/pixel"
|
|
90
|
+
return self._send_request(pixel_endpoint, {
|
|
91
|
+
'userAgent': input_data.user_agent,
|
|
92
|
+
'htmlVar': input_data.html_var,
|
|
93
|
+
'scriptVar': input_data.script_var,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
def generate_reese84_sensor(self, site: str, user_agent: str) -> str:
|
|
97
|
+
"""
|
|
98
|
+
Returns the sensor data required to generate valid reese84 cookies using the Hyper Solutions API.
|
|
99
|
+
|
|
100
|
+
This function sends a request to the specified sensor endpoint with the necessary data to generate the reese84 sensor data.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
site (str): The name of the site that will be used to generate the sensor data.
|
|
104
|
+
user_agent (str): The user agent string used to generate the sensor data.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
str: Sensor data as a string.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
ValueError: If the script attribute in input_data is empty.
|
|
111
|
+
"""
|
|
112
|
+
return self._send_request("https://incapsula.justhyped.dev/reese84/" + quote(site),
|
|
113
|
+
{
|
|
114
|
+
'userAgent': user_agent,
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
def generate_utmvc_cookie(self, input_data: UtmvcInput) -> str:
|
|
118
|
+
"""
|
|
119
|
+
Returns the utmvc cookie using the Hyper Solutions API.
|
|
120
|
+
|
|
121
|
+
This function sends a request to the utmvc sensor endpoint with the necessary data to generate the utmvc cookie.
|
|
122
|
+
The input data must include a non-empty script and session IDs.
|
|
123
|
+
|
|
124
|
+
Args:
|
|
125
|
+
session (Session): An instance of Session to handle the network request.
|
|
126
|
+
input_data (Input): An instance of Input containing the user agent, session IDs, and script.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
str: The utmvc cookie as a string.
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
ValueError: If the script attribute or session IDs in input_data are empty.
|
|
133
|
+
"""
|
|
134
|
+
if not input_data.script:
|
|
135
|
+
raise ValueError("script must be non empty")
|
|
136
|
+
|
|
137
|
+
if not input_data.session_ids:
|
|
138
|
+
raise ValueError("no session ids set")
|
|
139
|
+
|
|
140
|
+
return self._send_request("https://incapsula.justhyped.dev/utmvc", {
|
|
141
|
+
'userAgent': input_data.user_agent,
|
|
142
|
+
'sessionIds': input_data.session_ids,
|
|
143
|
+
'script': input_data.script,
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
def generate_kasada_pow(self, input_data: KasadaPowInput) -> str:
|
|
147
|
+
"""
|
|
148
|
+
Returns the x-kpsdk-cd value using the Hyper Solutions API.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
session (Session): An instance of Session to handle the network request.
|
|
152
|
+
input_data (Input): An instance of Input containing the st and optionally workTime.
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
str: The x-kpsdk-cd value as a string.
|
|
156
|
+
"""
|
|
157
|
+
return self._send_request("https://kasada.justhyped.dev/cd", input_data.to_dict())
|
|
158
|
+
|
|
159
|
+
def generate_kasada_payload(self, input_data: KasadaPayloadInput) -> tuple[str, dict]:
|
|
160
|
+
"""
|
|
161
|
+
Returns a base64 encoded payload and headers using the Hyper Solutions API.
|
|
162
|
+
|
|
163
|
+
Args: input_data (KasadaPayloadInput): An instance of KasadaPayloadInput containing the userAgent,
|
|
164
|
+
ipsLink and script.
|
|
165
|
+
|
|
166
|
+
Returns: tuple[str, dict]: A tuple containing the base64 encoded payload (to POST to /tl) as a string and a
|
|
167
|
+
dictionary of headers.
|
|
168
|
+
"""
|
|
169
|
+
if not self.api_key:
|
|
170
|
+
raise ValueError("Missing API key")
|
|
171
|
+
|
|
172
|
+
headers = {
|
|
173
|
+
'Content-Type': 'application/json',
|
|
174
|
+
'Accept-Encoding': 'gzip',
|
|
175
|
+
'X-Api-Key': self.api_key
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if self.jwt_key:
|
|
179
|
+
signature = self.generate_signature()
|
|
180
|
+
headers['X-Signature'] = signature
|
|
181
|
+
|
|
182
|
+
response = self.client.post("https://kasada.justhyped.dev/payload", headers=headers, json=input_data.to_dict())
|
|
183
|
+
|
|
184
|
+
response_data = response.json()
|
|
185
|
+
|
|
186
|
+
if "error" in response_data and response_data["error"]:
|
|
187
|
+
raise Exception(f"API returned with error: {response_data['error']}")
|
|
188
|
+
|
|
189
|
+
if response.status_code != 200:
|
|
190
|
+
raise Exception(f"API returned with status code: {response.status_code}")
|
|
191
|
+
|
|
192
|
+
return response_data["payload"], response_data["headers"]
|
|
193
|
+
|
|
194
|
+
def generate_interstitial_payload(self, input_data: DataDomeInterstitialInput) -> str:
|
|
195
|
+
"""
|
|
196
|
+
Returns the DataDome interstitial payload value using the Hyper Solutions API.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
session (Session): An instance of Session to handle the network request.
|
|
200
|
+
input_data (DataDomeInterstitialInput): An instance of DataDomeInterstitialInput.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
str: The payload to post to /interstitial/
|
|
204
|
+
"""
|
|
205
|
+
return self._send_request("https://datadome.justhyped.dev/interstitial", input_data.to_dict())
|
|
206
|
+
|
|
207
|
+
def generate_slider_payload(self, input_data: DataDomeSliderInput) -> str:
|
|
208
|
+
"""
|
|
209
|
+
Returns the DataDome Slider URL value using the Hyper Solutions API.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
session (Session): An instance of Session to handle the network request.
|
|
213
|
+
input_data (DataDomeSliderInput): An instance of DataDomeSliderInput.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
str: The URL to make a GET request to and returns a solved datadome cookie.
|
|
217
|
+
"""
|
|
218
|
+
return self._send_request("https://datadome.justhyped.dev/slider", input_data.to_dict())
|
|
219
|
+
|
|
220
|
+
def generate_tags_payload(self, input_data: DataDomeTagsInput) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Returns the DataDome Tags payload using the Hyper Solutions API.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
session (Session): An instance of Session to handle the network request.
|
|
226
|
+
input_data (DataDomeTagsInput): An instance of DataDomeTagsInput.
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
str: The tags payload.
|
|
230
|
+
"""
|
|
231
|
+
return self._send_request("https://datadome.justhyped.dev/tags", input_data.to_dict())
|
|
232
|
+
|
|
233
|
+
def generate_signature(self) -> str:
|
|
234
|
+
claims = {
|
|
235
|
+
"key": self.api_key,
|
|
236
|
+
"exp": datetime.now(timezone.utc) + timedelta(seconds=60)
|
|
237
|
+
}
|
|
238
|
+
token = jwt.encode(claims, self.jwt_key, algorithm='HS256')
|
|
239
|
+
return token.decode('utf-8')
|
|
240
|
+
|
|
241
|
+
def _send_request(self, url: str, input_data: Dict[str, Any]) -> str:
|
|
242
|
+
if not self.api_key:
|
|
243
|
+
raise ValueError("Missing API key")
|
|
244
|
+
|
|
245
|
+
headers = {
|
|
246
|
+
'Content-Type': 'application/json',
|
|
247
|
+
'Accept-Encoding': 'gzip',
|
|
248
|
+
'X-Api-Key': self.api_key
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if self.jwt_key:
|
|
252
|
+
signature = self.generate_signature()
|
|
253
|
+
headers['X-Signature'] = signature
|
|
254
|
+
|
|
255
|
+
response = self.client.post(url, headers=headers, json=input_data)
|
|
256
|
+
|
|
257
|
+
response_data = response.json()
|
|
258
|
+
|
|
259
|
+
if "error" in response_data and response_data["error"]:
|
|
260
|
+
raise Exception(f"API returned with error: {response_data['error']}")
|
|
261
|
+
|
|
262
|
+
if response.status_code != 200:
|
|
263
|
+
raise Exception(f"API returned with status code: {response.status_code}")
|
|
264
|
+
|
|
265
|
+
return response_data["payload"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: hyper_sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Hyper Solutions Python SDK
|
|
5
5
|
Project-URL: homepage, https://github.com/Hyper-Solutions/hyper-sdk-py
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -192,3 +192,99 @@ from hyper_sdk.incapsula.utmvc import get_utmvc_submit_path
|
|
|
192
192
|
|
|
193
193
|
submit_path = get_utmvc_submit_path()
|
|
194
194
|
```
|
|
195
|
+
|
|
196
|
+
## Kasada
|
|
197
|
+
|
|
198
|
+
The Kasada package provides functions for interacting with Kasada Bot Manager, including parsing script path.
|
|
199
|
+
|
|
200
|
+
### Generating Payload Data (CT)
|
|
201
|
+
|
|
202
|
+
To generate payload data required for generating valid `x-kpsdk-ct` tokens, use the `generate_kasada_payload` function:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
payload, headers = hyper_session.generate_kasada_payload(hyper_sdk.KasadaPayloadInput(
|
|
206
|
+
user_agent=USER_AGENT,
|
|
207
|
+
ips_link=ips_link,
|
|
208
|
+
script=ips_script,
|
|
209
|
+
language="en-US"
|
|
210
|
+
))
|
|
211
|
+
# Use payload and headers for the next request
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Generating Pow Data (CD)
|
|
215
|
+
|
|
216
|
+
To generate POW data (`x-kpsdk-cd`) tokens, use the `generate_kasada_pow` function:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
cd = hyper_session.generate_kasada_pow(hyper_sdk.KasadaPowInput(
|
|
220
|
+
st=st,
|
|
221
|
+
work_time=None
|
|
222
|
+
))
|
|
223
|
+
# Use cd as the x-kpsdk-cd header in the next request
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Parsing Script Path
|
|
227
|
+
|
|
228
|
+
To parse the Kasada script path from the given blocked page (status code 429) HTML code, use the `parse_script_path` function:
|
|
229
|
+
|
|
230
|
+
```python
|
|
231
|
+
from hyper_sdk.kasada import parse_script_path
|
|
232
|
+
|
|
233
|
+
script_path = parse_script_path(html_content)
|
|
234
|
+
# script_path will look like: /ips.js?...
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## DataDome
|
|
238
|
+
|
|
239
|
+
The DataDome package provides functions for interacting with DataDome Bot Manager, including parsing device link URLs
|
|
240
|
+
for interstitial and slider challenges.
|
|
241
|
+
|
|
242
|
+
### Generating Interstitial Payload
|
|
243
|
+
|
|
244
|
+
To generate payload data required for solving interstitial challenges, use the `generate_interstitial_payload` function:
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
payload = hyper_session.generate_interstitial_payload(hyper_sdk.DataDomeInterstitialInput(
|
|
248
|
+
user_agent=USER_AGENT,
|
|
249
|
+
device_link=device_check_link,
|
|
250
|
+
html=html_content
|
|
251
|
+
))
|
|
252
|
+
# Use the payload to POST to https://geo.captcha-delivery.com/interstitial/
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Generating Slider Payload
|
|
256
|
+
|
|
257
|
+
To solve DataDome Slider challenges, use the `generate_slider_payload` function:
|
|
258
|
+
|
|
259
|
+
```python
|
|
260
|
+
payload = hyper_session.generate_slider_payload(hyper_sdk.DataDomeSliderInput(
|
|
261
|
+
user_agent=USER_AGENT,
|
|
262
|
+
device_link=device_check_link,
|
|
263
|
+
html=html_content,
|
|
264
|
+
puzzle=base64_encoded_puzzle,
|
|
265
|
+
piece=base64_encoded_piece
|
|
266
|
+
))
|
|
267
|
+
# Create a GET request to the payload URL
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Parsing Interstitial DeviceLink URL
|
|
271
|
+
|
|
272
|
+
To parse the Interstitial DeviceLink URL from the HTML code, use the `parse_interstitial_device_check_link` function:
|
|
273
|
+
|
|
274
|
+
```python
|
|
275
|
+
from hyper_sdk.datadome import parse_interstitial_device_check_link
|
|
276
|
+
|
|
277
|
+
device_link = parse_interstitial_device_check_link(html_content, datadome_cookie, referer)
|
|
278
|
+
# device_link will look like: https://geo.captcha-delivery.com/interstitial/?...
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### Parsing Slider DeviceLink URL
|
|
282
|
+
|
|
283
|
+
To parse the Slider DeviceLink URL from the HTML code, use the `parse_slider_device_check_link` function:
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
from hyper_sdk.datadome import parse_slider_device_check_link
|
|
287
|
+
|
|
288
|
+
device_link = parse_slider_device_check_link(html_content, datadome_cookie, referer)
|
|
289
|
+
# device_link will look like: https://geo.captcha-delivery.com/captcha/?...
|
|
290
|
+
```
|
|
@@ -3,7 +3,9 @@ README.md
|
|
|
3
3
|
pyproject.toml
|
|
4
4
|
hyper_sdk/__init__.py
|
|
5
5
|
hyper_sdk/akamai_input.py
|
|
6
|
+
hyper_sdk/datadome_input.py
|
|
6
7
|
hyper_sdk/incapsula_input.py
|
|
8
|
+
hyper_sdk/kasada_input.py
|
|
7
9
|
hyper_sdk/session.py
|
|
8
10
|
hyper_sdk.egg-info/PKG-INFO
|
|
9
11
|
hyper_sdk.egg-info/SOURCES.txt
|
|
@@ -15,5 +17,9 @@ hyper_sdk/akamai/pixel.py
|
|
|
15
17
|
hyper_sdk/akamai/script_path.py
|
|
16
18
|
hyper_sdk/akamai/sec_cpt.py
|
|
17
19
|
hyper_sdk/akamai/stop_signal.py
|
|
20
|
+
hyper_sdk/datadome/__init__.py
|
|
21
|
+
hyper_sdk/datadome/parse.py
|
|
18
22
|
hyper_sdk/incapsula/__init__.py
|
|
19
|
-
hyper_sdk/incapsula/utmvc.py
|
|
23
|
+
hyper_sdk/incapsula/utmvc.py
|
|
24
|
+
hyper_sdk/kasada/__init__.py
|
|
25
|
+
hyper_sdk/kasada/parse.py
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hyper_sdk"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Hyper Solutions Python SDK"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.6"
|
|
@@ -27,4 +27,4 @@ dependencies = [
|
|
|
27
27
|
homepage = "https://github.com/Hyper-Solutions/hyper-sdk-py"
|
|
28
28
|
|
|
29
29
|
[tool.setuptools]
|
|
30
|
-
packages = ["hyper_sdk", "hyper_sdk.akamai", "hyper_sdk.incapsula"]
|
|
30
|
+
packages = ["hyper_sdk", "hyper_sdk.akamai", "hyper_sdk.incapsula", "hyper_sdk.kasada", "hyper_sdk.datadome"]
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
class SensorInput:
|
|
2
|
-
def __init__(self, abck: str, bmsz: str, version: str, page_url: str, user_agent: str, script_hash=""):
|
|
3
|
-
self.abck = abck
|
|
4
|
-
self.bmsz = bmsz
|
|
5
|
-
self.version = version
|
|
6
|
-
self.page_url = page_url
|
|
7
|
-
self.user_agent = user_agent
|
|
8
|
-
self.script_hash = script_hash
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class PixelInput:
|
|
12
|
-
def __init__(self, user_agent: str, html_var: str, script_var: str):
|
|
13
|
-
self.user_agent = user_agent
|
|
14
|
-
self.html_var = html_var
|
|
15
|
-
self.script_var = script_var
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
from typing import Optional, Dict, Any
|
|
2
|
-
from urllib.parse import quote
|
|
3
|
-
|
|
4
|
-
import requests
|
|
5
|
-
import jwt
|
|
6
|
-
from datetime import datetime, timedelta, timezone
|
|
7
|
-
|
|
8
|
-
from .akamai_input import SensorInput, PixelInput
|
|
9
|
-
from .incapsula_input import UtmvcInput
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Session:
|
|
13
|
-
def __init__(self, api_key: str, jwt_key: Optional[str] = None, client: Optional[requests.Session] = None) -> None:
|
|
14
|
-
self.api_key = api_key
|
|
15
|
-
self.jwt_key = jwt_key
|
|
16
|
-
self.client = requests.Session() if client is None else client
|
|
17
|
-
|
|
18
|
-
def generate_sensor_data(self, input_data: SensorInput) -> str:
|
|
19
|
-
"""
|
|
20
|
-
Returns the sensor data required to generate valid akamai cookies using the Hyper Solutions API.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
input_data (SensorInput): An instance of SensorInput containing the necessary data for generating the sensor data.
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
str: Sensor data as a string.
|
|
27
|
-
"""
|
|
28
|
-
sensor_endpoint = "https://akm.justhyped.dev/sensor"
|
|
29
|
-
return self._send_request(sensor_endpoint, {
|
|
30
|
-
'userAgent': input_data.user_agent,
|
|
31
|
-
'abck': input_data.abck,
|
|
32
|
-
'bmsz': input_data.bmsz,
|
|
33
|
-
'version': input_data.version,
|
|
34
|
-
'pageUrl': input_data.page_url,
|
|
35
|
-
'scriptHash': input_data.script_hash,
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
def generate_pixel_data(self, input_data: PixelInput) -> str:
|
|
39
|
-
"""
|
|
40
|
-
Returns the pixel data using the Hyper Solutions API.
|
|
41
|
-
|
|
42
|
-
Args:
|
|
43
|
-
session (Session): An instance of Session to handle the network request.
|
|
44
|
-
input_data (PixelInput): An instance of PixelInput containing the necessary data for generating the pixel data.
|
|
45
|
-
|
|
46
|
-
Returns:
|
|
47
|
-
str: Pixel data as a string.
|
|
48
|
-
"""
|
|
49
|
-
pixel_endpoint = "https://akm.justhyped.dev/pixel"
|
|
50
|
-
return self._send_request(pixel_endpoint, {
|
|
51
|
-
'userAgent': input_data.user_agent,
|
|
52
|
-
'htmlVar': input_data.html_var,
|
|
53
|
-
'scriptVar': input_data.script_var,
|
|
54
|
-
})
|
|
55
|
-
|
|
56
|
-
def generate_reese84_sensor(self, site: str, user_agent: str) -> str:
|
|
57
|
-
"""
|
|
58
|
-
Returns the sensor data required to generate valid reese84 cookies using the Hyper Solutions API.
|
|
59
|
-
|
|
60
|
-
This function sends a request to the specified sensor endpoint with the necessary data to generate the reese84 sensor data.
|
|
61
|
-
|
|
62
|
-
Args:
|
|
63
|
-
site (str): The name of the site that will be used to generate the sensor data.
|
|
64
|
-
user_agent (str): The user agent string used to generate the sensor data.
|
|
65
|
-
|
|
66
|
-
Returns:
|
|
67
|
-
str: Sensor data as a string.
|
|
68
|
-
|
|
69
|
-
Raises:
|
|
70
|
-
ValueError: If the script attribute in input_data is empty.
|
|
71
|
-
"""
|
|
72
|
-
return self._send_request("https://incapsula.justhyped.dev/reese84/" + quote(site),
|
|
73
|
-
{
|
|
74
|
-
'userAgent': user_agent,
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
def generate_utmvc_cookie(self, input_data: UtmvcInput) -> str:
|
|
78
|
-
"""
|
|
79
|
-
Returns the utmvc cookie using the Hyper Solutions API.
|
|
80
|
-
|
|
81
|
-
This function sends a request to the utmvc sensor endpoint with the necessary data to generate the utmvc cookie.
|
|
82
|
-
The input data must include a non-empty script and session IDs.
|
|
83
|
-
|
|
84
|
-
Args:
|
|
85
|
-
session (Session): An instance of Session to handle the network request.
|
|
86
|
-
input_data (Input): An instance of Input containing the user agent, session IDs, and script.
|
|
87
|
-
|
|
88
|
-
Returns:
|
|
89
|
-
str: The utmvc cookie as a string.
|
|
90
|
-
|
|
91
|
-
Raises:
|
|
92
|
-
ValueError: If the script attribute or session IDs in input_data are empty.
|
|
93
|
-
"""
|
|
94
|
-
if not input_data.script:
|
|
95
|
-
raise ValueError("script must be non empty")
|
|
96
|
-
|
|
97
|
-
if not input_data.session_ids:
|
|
98
|
-
raise ValueError("no session ids set")
|
|
99
|
-
|
|
100
|
-
return self._send_request("https://incapsula.justhyped.dev/utmvc", {
|
|
101
|
-
'userAgent': input_data.user_agent,
|
|
102
|
-
'sessionIds': input_data.session_ids,
|
|
103
|
-
'script': input_data.script,
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
def generate_signature(self) -> str:
|
|
107
|
-
claims = {
|
|
108
|
-
"key": self.api_key,
|
|
109
|
-
"exp": datetime.now(timezone.utc) + timedelta(seconds=60)
|
|
110
|
-
}
|
|
111
|
-
token = jwt.encode(claims, self.jwt_key, algorithm='HS256')
|
|
112
|
-
return token.decode('utf-8')
|
|
113
|
-
|
|
114
|
-
def _send_request(self, url: str, input_data: Dict[str, Any]) -> str:
|
|
115
|
-
if not self.api_key:
|
|
116
|
-
raise ValueError("Missing API key")
|
|
117
|
-
|
|
118
|
-
headers = {
|
|
119
|
-
'Content-Type': 'application/json',
|
|
120
|
-
'Accept-Encoding': 'gzip',
|
|
121
|
-
'X-Api-Key': self.api_key
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
if self.jwt_key:
|
|
125
|
-
signature = self.generate_signature()
|
|
126
|
-
headers['X-Signature'] = signature
|
|
127
|
-
|
|
128
|
-
response = self.client.post(url, headers=headers, json=input_data)
|
|
129
|
-
|
|
130
|
-
response_data = response.json()
|
|
131
|
-
|
|
132
|
-
if "error" in response_data and response_data["error"]:
|
|
133
|
-
raise Exception(f"API returned with error: {response_data['error']}")
|
|
134
|
-
|
|
135
|
-
if response.status_code != 200:
|
|
136
|
-
raise Exception(f"API returned with status code: {response.status_code}")
|
|
137
|
-
|
|
138
|
-
return response_data["payload"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|