hyper-sdk 2.12.5__tar.gz → 2.13.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-2.12.5 → hyper_sdk-2.13.0}/PKG-INFO +29 -1
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/README.md +28 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/datadome/parse.py +56 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/datadome_input.py +29 -4
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk.egg-info/PKG-INFO +29 -1
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/pyproject.toml +37 -37
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/LICENSE +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/__init__.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/akamai/__init__.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/akamai/pixel.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/akamai/script_path.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/akamai/sec_cpt.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/akamai/stop_signal.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/akamai_input.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/datadome/__init__.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/incapsula/__init__.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/incapsula/dynamic.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/incapsula/utmvc.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/incapsula_input.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/kasada/__init__.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/kasada/parse.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/kasada_input.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/session.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/session_async.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/shared.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk/trustdecision_input.py +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk.egg-info/SOURCES.txt +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk.egg-info/dependency_links.txt +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk.egg-info/requires.txt +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/hyper_sdk.egg-info/top_level.txt +0 -0
- {hyper_sdk-2.12.5 → hyper_sdk-2.13.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyper_sdk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.13.0
|
|
4
4
|
Summary: Hyper Solutions Python SDK
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -386,6 +386,34 @@ device_link = parse_slider_device_check_link(
|
|
|
386
386
|
)
|
|
387
387
|
```
|
|
388
388
|
|
|
389
|
+
### Challenge Script
|
|
390
|
+
|
|
391
|
+
DataDome sometimes serves a challenge page with the **challenge script in its own file** instead of inlining it in the page:
|
|
392
|
+
|
|
393
|
+
```html
|
|
394
|
+
<script defer src="https://ct.captcha-delivery.com/interstitial.1.33.0.202609141.js"></script>
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
It switches between the two forms per request, so check every challenge page. When the page uses a separate file, fetch it with your own client so the request keeps your proxy, TLS fingerprint and headers, then pass the body as `script`:
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
from hyper_sdk import DataDomeInterstitialInput
|
|
401
|
+
from hyper_sdk.datadome import parse_challenge_script_url
|
|
402
|
+
|
|
403
|
+
script = ""
|
|
404
|
+
script_url = parse_challenge_script_url(html_content)
|
|
405
|
+
if script_url is not None:
|
|
406
|
+
script = your_client.get(script_url).text
|
|
407
|
+
|
|
408
|
+
result = session.generate_interstitial_payload(DataDomeInterstitialInput(
|
|
409
|
+
# other interstitial input fields
|
|
410
|
+
html=html_content,
|
|
411
|
+
script=script, # empty when the page inlines the script
|
|
412
|
+
))
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
The same applies to `DataDomeSliderInput`. One helper covers both challenge types, since the captcha page uses the same tag. `script` is optional and leaving it empty keeps working.
|
|
416
|
+
|
|
389
417
|
## 📖 Documentation
|
|
390
418
|
|
|
391
419
|
For detailed documentation on how to use the SDK, including examples and API reference, please visit our documentation website:
|
|
@@ -343,6 +343,34 @@ device_link = parse_slider_device_check_link(
|
|
|
343
343
|
)
|
|
344
344
|
```
|
|
345
345
|
|
|
346
|
+
### Challenge Script
|
|
347
|
+
|
|
348
|
+
DataDome sometimes serves a challenge page with the **challenge script in its own file** instead of inlining it in the page:
|
|
349
|
+
|
|
350
|
+
```html
|
|
351
|
+
<script defer src="https://ct.captcha-delivery.com/interstitial.1.33.0.202609141.js"></script>
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
It switches between the two forms per request, so check every challenge page. When the page uses a separate file, fetch it with your own client so the request keeps your proxy, TLS fingerprint and headers, then pass the body as `script`:
|
|
355
|
+
|
|
356
|
+
```python
|
|
357
|
+
from hyper_sdk import DataDomeInterstitialInput
|
|
358
|
+
from hyper_sdk.datadome import parse_challenge_script_url
|
|
359
|
+
|
|
360
|
+
script = ""
|
|
361
|
+
script_url = parse_challenge_script_url(html_content)
|
|
362
|
+
if script_url is not None:
|
|
363
|
+
script = your_client.get(script_url).text
|
|
364
|
+
|
|
365
|
+
result = session.generate_interstitial_payload(DataDomeInterstitialInput(
|
|
366
|
+
# other interstitial input fields
|
|
367
|
+
html=html_content,
|
|
368
|
+
script=script, # empty when the page inlines the script
|
|
369
|
+
))
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
The same applies to `DataDomeSliderInput`. One helper covers both challenge types, since the captcha page uses the same tag. `script` is optional and leaving it empty keeps working.
|
|
373
|
+
|
|
346
374
|
## 📖 Documentation
|
|
347
375
|
|
|
348
376
|
For detailed documentation on how to use the SDK, including examples and API reference, please visit our documentation website:
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import json
|
|
2
|
+
import re
|
|
3
|
+
from typing import Optional
|
|
2
4
|
from urllib.parse import urlencode
|
|
3
5
|
|
|
6
|
+
# Matches the tag a challenge page uses to load the bundle from
|
|
7
|
+
# ct.captcha-delivery.com, capturing the URL. The name in front of the version
|
|
8
|
+
# is left open so it keeps matching whatever DataDome calls the bundle, e.g.
|
|
9
|
+
# interstitial.1.33.0.202609141.js or captcha.1.34.0.202609141.js.
|
|
10
|
+
_CHALLENGE_SCRIPT_REGEX = re.compile(
|
|
11
|
+
r"""<script[^>]+src\s*=\s*["']([^"']*/[a-z_-]+\.\d+\.\d+\.\d+\.[^"']*\.js)["']"""
|
|
12
|
+
)
|
|
13
|
+
|
|
4
14
|
|
|
5
15
|
def parse_slider_device_check_link(src: str, datadome_cookie: str, referer: str) -> str:
|
|
6
16
|
"""
|
|
@@ -82,3 +92,49 @@ def parse_interstitial_device_check_link(src: str, datadome_cookie: str, referer
|
|
|
82
92
|
}
|
|
83
93
|
|
|
84
94
|
return f"https://geo.captcha-delivery.com/interstitial/?{urlencode(params)}"
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def parse_challenge_script_url(html: str) -> Optional[str]:
|
|
98
|
+
"""
|
|
99
|
+
Return the challenge bundle URL a device check or captcha page loads with a
|
|
100
|
+
<script defer src="..."> tag, or None when the page has no such tag.
|
|
101
|
+
|
|
102
|
+
DataDome serves some challenge pages with the bundle inlined in the HTML and
|
|
103
|
+
others with it in its own file, switching between the two per request, so this
|
|
104
|
+
has to be checked on every challenge rather than configured once.
|
|
105
|
+
|
|
106
|
+
When it returns a URL, GET that URL with the same client, proxy and headers you
|
|
107
|
+
used for the challenge page, and pass the response body as the `script` field of
|
|
108
|
+
DataDomeInterstitialInput or DataDomeSliderInput. When it returns None the
|
|
109
|
+
bundle is already in the HTML and `script` stays empty.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
html (str): The response body of the GET request to the device check link.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Optional[str]: The challenge bundle URL, or None when the page inlines it.
|
|
116
|
+
|
|
117
|
+
Example:
|
|
118
|
+
device_link = parse_interstitial_device_check_link(body, cookie, referer)
|
|
119
|
+
# ... GET device_link with your own client, read the body into html ...
|
|
120
|
+
|
|
121
|
+
script = ""
|
|
122
|
+
script_url = parse_challenge_script_url(html)
|
|
123
|
+
if script_url is not None:
|
|
124
|
+
# fetch script_url with your own client, read the body into script
|
|
125
|
+
script = your_client.get(script_url).text
|
|
126
|
+
|
|
127
|
+
result = session.generate_interstitial_payload(DataDomeInterstitialInput(
|
|
128
|
+
user_agent=user_agent,
|
|
129
|
+
device_link=device_link,
|
|
130
|
+
html=html,
|
|
131
|
+
accept_language=accept_language,
|
|
132
|
+
ip=ip,
|
|
133
|
+
script=script,
|
|
134
|
+
))
|
|
135
|
+
"""
|
|
136
|
+
match = _CHALLENGE_SCRIPT_REGEX.search(html)
|
|
137
|
+
if match is None:
|
|
138
|
+
return None
|
|
139
|
+
|
|
140
|
+
return match.group(1)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
class DataDomeSliderInput:
|
|
3
|
-
def __init__(self, user_agent: str, device_link: str, html: str, puzzle: str, piece: str, parent_url: str, accept_language: str, ip: str):
|
|
3
|
+
def __init__(self, user_agent: str, device_link: str, html: str, puzzle: str, piece: str, parent_url: str, accept_language: str, ip: str, script: str = ""):
|
|
4
4
|
# UserAgent must be a Chrome Windows User-Agent.
|
|
5
5
|
self.user_agent = user_agent
|
|
6
6
|
|
|
@@ -22,11 +22,21 @@ class DataDomeSliderInput:
|
|
|
22
22
|
self.piece = piece
|
|
23
23
|
|
|
24
24
|
self.parent_url = parent_url
|
|
25
|
+
|
|
26
|
+
# Script is the DataDome challenge bundle. It is only needed when the
|
|
27
|
+
# challenge page loads the bundle with a <script defer src="..."> tag
|
|
28
|
+
# instead of inlining it, which DataDome does per request. Use
|
|
29
|
+
# hyper_sdk.datadome.parse_challenge_script_url on the html to find out,
|
|
30
|
+
# then GET that URL with the same client and pass the response body here.
|
|
31
|
+
#
|
|
32
|
+
# Leave it empty when the page inlines the bundle.
|
|
33
|
+
self.script = script
|
|
34
|
+
|
|
25
35
|
self.accept_language = accept_language
|
|
26
36
|
self.ip = ip
|
|
27
37
|
|
|
28
38
|
def to_dict(self):
|
|
29
|
-
|
|
39
|
+
data = {
|
|
30
40
|
"userAgent": self.user_agent,
|
|
31
41
|
"deviceLink": self.device_link,
|
|
32
42
|
"html": self.html,
|
|
@@ -36,10 +46,13 @@ class DataDomeSliderInput:
|
|
|
36
46
|
"acceptLanguage": self.accept_language,
|
|
37
47
|
"ip": self.ip,
|
|
38
48
|
}
|
|
49
|
+
if self.script: # Only include script if it's not empty
|
|
50
|
+
data["script"] = self.script
|
|
51
|
+
return data
|
|
39
52
|
|
|
40
53
|
|
|
41
54
|
class DataDomeInterstitialInput:
|
|
42
|
-
def __init__(self, user_agent: str, device_link: str, html: str, accept_language: str, ip: str):
|
|
55
|
+
def __init__(self, user_agent: str, device_link: str, html: str, accept_language: str, ip: str, script: str = ""):
|
|
43
56
|
# UserAgent must be a Chrome Windows User-Agent.
|
|
44
57
|
self.user_agent = user_agent
|
|
45
58
|
|
|
@@ -50,17 +63,29 @@ class DataDomeInterstitialInput:
|
|
|
50
63
|
# Html is the response body of the GET request to the DeviceLink
|
|
51
64
|
self.html = html
|
|
52
65
|
|
|
66
|
+
# Script is the DataDome challenge bundle. It is only needed when the
|
|
67
|
+
# challenge page loads the bundle with a <script defer src="..."> tag
|
|
68
|
+
# instead of inlining it, which DataDome does per request. Use
|
|
69
|
+
# hyper_sdk.datadome.parse_challenge_script_url on the html to find out,
|
|
70
|
+
# then GET that URL with the same client and pass the response body here.
|
|
71
|
+
#
|
|
72
|
+
# Leave it empty when the page inlines the bundle.
|
|
73
|
+
self.script = script
|
|
74
|
+
|
|
53
75
|
self.accept_language = accept_language
|
|
54
76
|
self.ip = ip
|
|
55
77
|
|
|
56
78
|
def to_dict(self):
|
|
57
|
-
|
|
79
|
+
data = {
|
|
58
80
|
"userAgent": self.user_agent,
|
|
59
81
|
"deviceLink": self.device_link,
|
|
60
82
|
"html": self.html,
|
|
61
83
|
"acceptLanguage": self.accept_language,
|
|
62
84
|
"ip": self.ip,
|
|
63
85
|
}
|
|
86
|
+
if self.script: # Only include script if it's not empty
|
|
87
|
+
data["script"] = self.script
|
|
88
|
+
return data
|
|
64
89
|
|
|
65
90
|
|
|
66
91
|
class DataDomeTagsInput:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyper_sdk
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.13.0
|
|
4
4
|
Summary: Hyper Solutions Python SDK
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -386,6 +386,34 @@ device_link = parse_slider_device_check_link(
|
|
|
386
386
|
)
|
|
387
387
|
```
|
|
388
388
|
|
|
389
|
+
### Challenge Script
|
|
390
|
+
|
|
391
|
+
DataDome sometimes serves a challenge page with the **challenge script in its own file** instead of inlining it in the page:
|
|
392
|
+
|
|
393
|
+
```html
|
|
394
|
+
<script defer src="https://ct.captcha-delivery.com/interstitial.1.33.0.202609141.js"></script>
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
It switches between the two forms per request, so check every challenge page. When the page uses a separate file, fetch it with your own client so the request keeps your proxy, TLS fingerprint and headers, then pass the body as `script`:
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
from hyper_sdk import DataDomeInterstitialInput
|
|
401
|
+
from hyper_sdk.datadome import parse_challenge_script_url
|
|
402
|
+
|
|
403
|
+
script = ""
|
|
404
|
+
script_url = parse_challenge_script_url(html_content)
|
|
405
|
+
if script_url is not None:
|
|
406
|
+
script = your_client.get(script_url).text
|
|
407
|
+
|
|
408
|
+
result = session.generate_interstitial_payload(DataDomeInterstitialInput(
|
|
409
|
+
# other interstitial input fields
|
|
410
|
+
html=html_content,
|
|
411
|
+
script=script, # empty when the page inlines the script
|
|
412
|
+
))
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
The same applies to `DataDomeSliderInput`. One helper covers both challenge types, since the captcha page uses the same tag. `script` is optional and leaving it empty keeps working.
|
|
416
|
+
|
|
389
417
|
## 📖 Documentation
|
|
390
418
|
|
|
391
419
|
For detailed documentation on how to use the SDK, including examples and API reference, please visit our documentation website:
|
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
[build-system]
|
|
2
|
-
requires = ["setuptools>=42", "wheel"]
|
|
3
|
-
build-backend = "setuptools.build_meta"
|
|
4
|
-
|
|
5
|
-
[project]
|
|
6
|
-
name = "hyper_sdk"
|
|
7
|
-
version = "2.
|
|
8
|
-
description = "Hyper Solutions Python SDK"
|
|
9
|
-
readme = "README.md"
|
|
10
|
-
requires-python = ">=3.7"
|
|
11
|
-
classifiers = [
|
|
12
|
-
"Intended Audience :: Developers",
|
|
13
|
-
"License :: OSI Approved :: MIT License",
|
|
14
|
-
"Programming Language :: Python :: 3",
|
|
15
|
-
"Framework :: AsyncIO",
|
|
16
|
-
]
|
|
17
|
-
dependencies = [
|
|
18
|
-
"httpx[http2]>=0.24.0",
|
|
19
|
-
"certifi>=2024.2.2",
|
|
20
|
-
"charset-normalizer>=3.3.2",
|
|
21
|
-
"idna>=3.6",
|
|
22
|
-
"jsonpickle>=3.0.3",
|
|
23
|
-
"PyJWT>=2.8.0",
|
|
24
|
-
"urllib3>=2.2.1"
|
|
25
|
-
]
|
|
26
|
-
license = {file = "LICENSE"}
|
|
27
|
-
|
|
28
|
-
[project.urls]
|
|
29
|
-
homepage = "https://github.com/Hyper-Solutions/hyper-sdk-py"
|
|
30
|
-
|
|
31
|
-
[tool.setuptools]
|
|
32
|
-
packages = [
|
|
33
|
-
"hyper_sdk",
|
|
34
|
-
"hyper_sdk.akamai",
|
|
35
|
-
"hyper_sdk.incapsula",
|
|
36
|
-
"hyper_sdk.kasada",
|
|
37
|
-
"hyper_sdk.datadome",
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hyper_sdk"
|
|
7
|
+
version = "2.13.0"
|
|
8
|
+
description = "Hyper Solutions Python SDK"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.7"
|
|
11
|
+
classifiers = [
|
|
12
|
+
"Intended Audience :: Developers",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Framework :: AsyncIO",
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"httpx[http2]>=0.24.0",
|
|
19
|
+
"certifi>=2024.2.2",
|
|
20
|
+
"charset-normalizer>=3.3.2",
|
|
21
|
+
"idna>=3.6",
|
|
22
|
+
"jsonpickle>=3.0.3",
|
|
23
|
+
"PyJWT>=2.8.0",
|
|
24
|
+
"urllib3>=2.2.1"
|
|
25
|
+
]
|
|
26
|
+
license = {file = "LICENSE"}
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
homepage = "https://github.com/Hyper-Solutions/hyper-sdk-py"
|
|
30
|
+
|
|
31
|
+
[tool.setuptools]
|
|
32
|
+
packages = [
|
|
33
|
+
"hyper_sdk",
|
|
34
|
+
"hyper_sdk.akamai",
|
|
35
|
+
"hyper_sdk.incapsula",
|
|
36
|
+
"hyper_sdk.kasada",
|
|
37
|
+
"hyper_sdk.datadome",
|
|
38
38
|
]
|
|
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
|
|
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
|
|
File without changes
|