hyper-sdk 2.7.0__tar.gz → 2.12.1__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.
Files changed (31) hide show
  1. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/PKG-INFO +26 -1
  2. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/README.md +25 -0
  3. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/akamai_input.py +10 -13
  4. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/datadome/parse.py +1 -0
  5. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/datadome_input.py +14 -13
  6. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/incapsula_input.py +4 -4
  7. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/kasada_input.py +30 -4
  8. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/session.py +38 -47
  9. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/session_async.py +38 -47
  10. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk.egg-info/PKG-INFO +26 -1
  11. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/pyproject.toml +2 -2
  12. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/LICENSE +0 -0
  13. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/__init__.py +0 -0
  14. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/akamai/__init__.py +0 -0
  15. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/akamai/pixel.py +0 -0
  16. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/akamai/script_path.py +0 -0
  17. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/akamai/sec_cpt.py +0 -0
  18. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/akamai/stop_signal.py +0 -0
  19. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/datadome/__init__.py +0 -0
  20. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/incapsula/__init__.py +0 -0
  21. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/incapsula/dynamic.py +0 -0
  22. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/incapsula/utmvc.py +0 -0
  23. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/kasada/__init__.py +0 -0
  24. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/kasada/parse.py +0 -0
  25. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/shared.py +0 -0
  26. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk/trustdecision_input.py +0 -0
  27. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk.egg-info/SOURCES.txt +0 -0
  28. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk.egg-info/dependency_links.txt +0 -0
  29. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk.egg-info/requires.txt +0 -0
  30. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/hyper_sdk.egg-info/top_level.txt +0 -0
  31. {hyper_sdk-2.7.0 → hyper_sdk-2.12.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyper_sdk
3
- Version: 2.7.0
3
+ Version: 2.12.1
4
4
  Summary: Hyper Solutions Python SDK
5
5
  License: MIT License
6
6
 
@@ -280,6 +280,31 @@ pow_payload = session.generate_kasada_pow(KasadaPowInput(
280
280
  ))
281
281
  ```
282
282
 
283
+ ## 🤖 Vercel BotID
284
+
285
+ Bypass **Vercel BotID** protection by generating the required `x-is-human` header.
286
+
287
+ ### Generating the x-is-human Header
288
+
289
+ Create the **x-is-human header** for Vercel BotID bypass:
290
+
291
+ ```python
292
+ from hyper_sdk import BotIDHeaderInput
293
+
294
+ header = session.generate_botid_header(BotIDHeaderInput(
295
+ script=script_body, # The c.js script content
296
+ user_agent="your-user-agent",
297
+ ip="your-proxy-ip",
298
+ accept_language="en-US,en;q=0.9",
299
+ ))
300
+
301
+ # Use the header in your requests
302
+ headers = {
303
+ "x-is-human": header,
304
+ # ... other headers
305
+ }
306
+ ```
307
+
283
308
  ### Script Path Extraction
284
309
 
285
310
  Extract **Kasada script paths** from blocked pages (HTTP 429):
@@ -237,6 +237,31 @@ pow_payload = session.generate_kasada_pow(KasadaPowInput(
237
237
  ))
238
238
  ```
239
239
 
240
+ ## 🤖 Vercel BotID
241
+
242
+ Bypass **Vercel BotID** protection by generating the required `x-is-human` header.
243
+
244
+ ### Generating the x-is-human Header
245
+
246
+ Create the **x-is-human header** for Vercel BotID bypass:
247
+
248
+ ```python
249
+ from hyper_sdk import BotIDHeaderInput
250
+
251
+ header = session.generate_botid_header(BotIDHeaderInput(
252
+ script=script_body, # The c.js script content
253
+ user_agent="your-user-agent",
254
+ ip="your-proxy-ip",
255
+ accept_language="en-US,en;q=0.9",
256
+ ))
257
+
258
+ # Use the header in your requests
259
+ headers = {
260
+ "x-is-human": header,
261
+ # ... other headers
262
+ }
263
+ ```
264
+
240
265
  ### Script Path Extraction
241
266
 
242
267
  Extract **Kasada script paths** from blocked pages (HTTP 429):
@@ -1,38 +1,35 @@
1
1
  class SensorInput:
2
- def __init__(self, abck: str, bmsz: str, version: str, page_url: str, user_agent: str, ip: str, acceptLanguage: str, context: str, script_hash="", dynamic_values=""):
2
+ def __init__(self, abck: str, bmsz: str, version: str, page_url: str, user_agent: str, ip: str, accept_language: str,
3
+ context: str, script: str, script_url: str):
3
4
  self.abck = abck
4
5
  self.bmsz = bmsz
5
6
  self.version = version
6
7
  self.page_url = page_url
7
8
  self.user_agent = user_agent
8
- self.script_hash = script_hash
9
- self.dynamic_values = dynamic_values
9
+ self.script = script
10
+ self.script_url = script_url
10
11
  self.context = context
11
12
  self.ip = ip
12
- self.acceptLanguage = acceptLanguage
13
+ self.accept_language = accept_language
13
14
 
14
15
 
15
16
  class PixelInput:
16
- def __init__(self, user_agent: str, html_var: str, script_var: str, acceptLanguage: str, ip: str):
17
+ def __init__(self, user_agent: str, html_var: str, script_var: str, accept_language: str, ip: str):
17
18
  self.user_agent = user_agent
18
19
  self.html_var = html_var
19
20
  self.script_var = script_var
20
- self.acceptLanguage = acceptLanguage
21
+ self.accept_language = accept_language
21
22
  self.ip = ip
22
23
 
23
24
 
24
- class DynamicInput:
25
- def __init__(self, script: str):
26
- self.script = script
27
-
28
-
29
25
  class SbsdInput:
30
- def __init__(self, index: int, user_agent: str, uuid: str, page_url: str, o_cookie: str, script: str, acceptLanguage: str, ip: str):
26
+ def __init__(self, index: int, user_agent: str, uuid: str, page_url: str, o_cookie: str, script: str,
27
+ accept_language: str, ip: str):
31
28
  self.index = index
32
29
  self.user_agent = user_agent
33
30
  self.uuid = uuid
34
31
  self.page_url = page_url
35
32
  self.o_cookie = o_cookie
36
33
  self.script = script
37
- self.acceptLanguage = acceptLanguage
34
+ self.accept_language = accept_language
38
35
  self.ip = ip
@@ -76,6 +76,7 @@ def parse_interstitial_device_check_link(src: str, datadome_cookie: str, referer
76
76
  "cid": datadome_cookie,
77
77
  "referer": referer,
78
78
  "s": str(dd_object_parsed.get("s")),
79
+ "e": str(dd_object_parsed.get("e")),
79
80
  "b": str(dd_object_parsed.get("b")),
80
81
  "dm": "cd",
81
82
  }
@@ -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, acceptLanguage: 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):
4
4
  # UserAgent must be a Chrome Windows User-Agent.
5
5
  self.user_agent = user_agent
6
6
 
@@ -22,7 +22,7 @@ class DataDomeSliderInput:
22
22
  self.piece = piece
23
23
 
24
24
  self.parent_url = parent_url
25
- self.acceptLanguage = acceptLanguage
25
+ self.accept_language = accept_language
26
26
  self.ip = ip
27
27
 
28
28
  def to_dict(self):
@@ -33,13 +33,13 @@ class DataDomeSliderInput:
33
33
  "puzzle": self.puzzle,
34
34
  "piece": self.piece,
35
35
  "parentUrl": self.parent_url,
36
- "acceptLanguage": self.acceptLanguage,
36
+ "acceptLanguage": self.accept_language,
37
37
  "ip": self.ip,
38
38
  }
39
39
 
40
40
 
41
41
  class DataDomeInterstitialInput:
42
- def __init__(self, user_agent: str, device_link: str, html: str, acceptLanguage: str, ip: str):
42
+ def __init__(self, user_agent: str, device_link: str, html: str, accept_language: str, ip: str):
43
43
  # UserAgent must be a Chrome Windows User-Agent.
44
44
  self.user_agent = user_agent
45
45
 
@@ -50,7 +50,7 @@ class DataDomeInterstitialInput:
50
50
  # Html is the response body of the GET request to the DeviceLink
51
51
  self.html = html
52
52
 
53
- self.acceptLanguage = acceptLanguage
53
+ self.accept_language = accept_language
54
54
  self.ip = ip
55
55
 
56
56
  def to_dict(self):
@@ -58,31 +58,32 @@ class DataDomeInterstitialInput:
58
58
  "userAgent": self.user_agent,
59
59
  "deviceLink": self.device_link,
60
60
  "html": self.html,
61
- "acceptLanguage": self.acceptLanguage,
61
+ "acceptLanguage": self.accept_language,
62
62
  "ip": self.ip,
63
63
  }
64
64
 
65
65
 
66
66
  class DataDomeTagsInput:
67
- def __init__(self, user_agent: str, cid: str, ddk: str, referer: str, tags_type: str, version: str, acceptLanguage: str, ip: str):
68
- # UserAgent must be a Chrome Windows User-Agent.
67
+ def __init__(self, user_agent: str, ddk: str, referer: str, tags_type: str, version: str, accept_language: str, ip: str, cid: str = ""):
69
68
  self.user_agent = user_agent
70
69
  self.cid = cid
71
70
  self.ddk = ddk
72
71
  self.referer = referer
73
72
  self.tags_type = tags_type
74
73
  self.version = version
75
- self.acceptLanguage = acceptLanguage
74
+ self.accept_language = accept_language
76
75
  self.ip = ip
77
76
 
78
77
  def to_dict(self):
79
- return {
78
+ data = {
80
79
  "userAgent": self.user_agent,
81
- "cid": self.cid,
82
80
  "ddk": self.ddk,
83
81
  "referer": self.referer,
84
82
  "type": self.tags_type,
85
- "acceptLanguage": self.acceptLanguage,
83
+ "acceptLanguage": self.accept_language,
86
84
  "ip": self.ip,
87
85
  "version": self.version,
88
- }
86
+ }
87
+ if self.cid: # Only include cid if it's not empty
88
+ data["cid"] = self.cid
89
+ return data
@@ -8,11 +8,11 @@ class UtmvcInput:
8
8
  self.script = script
9
9
 
10
10
  class ReeseInput:
11
- def __init__(self, user_agent: str, acceptLanguage: str, ip: str, pageUrl: str, pow: str, script: str, scriptUrl: str = ""):
11
+ def __init__(self, user_agent: str, accept_language: str, ip: str, pageUrl: str, script: str, script_url: str, pow: str = ""):
12
12
  self.user_agent = user_agent
13
- self.acceptLanguage = acceptLanguage
13
+ self.accept_language = accept_language
14
14
  self.ip = ip
15
- self.scriptUrl = scriptUrl
15
+ self.script_url = script_url
16
16
  self.pow = pow
17
17
  self.script = script
18
- self.pageUrl = pageUrl
18
+ self.pageUrl = pageUrl
@@ -1,22 +1,26 @@
1
1
  class KasadaPowInput:
2
- def __init__(self, st: int, ct: str, domain: str, work_time: int = None):
2
+ def __init__(self, st: int, ct: str, domain: str, fc: str = "", work_time: int = None):
3
3
  # St is the x-kpsdk-st value returned by the /tl POST request
4
4
  self.st = st
5
5
  # Ct is the x-kpsdk-ct value returned by the /tl POST request
6
6
  self.ct = ct
7
+ # fc is the x-kpsdk-fc value returned by the /mfc GET request, if used by the site
8
+ self.fc = fc
7
9
  # WorkTime can be used to pre-generate POW strings
8
10
  self.work_time = work_time
9
11
  self.domain = domain
10
12
 
11
13
  def to_dict(self):
12
14
  result = {"st": self.st, "ct": self.ct, "domain": self.domain}
15
+ if self.fc: # Only include fc if it's not empty
16
+ result["fc"] = self.fc
13
17
  if self.work_time is not None:
14
18
  result["workTime"] = self.work_time
15
19
  return result
16
20
 
17
21
 
18
22
  class KasadaPayloadInput:
19
- def __init__(self, user_agent: str, ips_link: str, script: str, acceptLanguage: str, ip: str):
23
+ def __init__(self, user_agent: str, ips_link: str, script: str, accept_language: str, ip: str):
20
24
  # UserAgent must be a Chrome Windows User-Agent.
21
25
  self.user_agent = user_agent
22
26
 
@@ -27,7 +31,7 @@ class KasadaPayloadInput:
27
31
  self.script = script
28
32
 
29
33
  # Your accept-language header
30
- self.acceptLanguage = acceptLanguage
34
+ self.accept_language = accept_language
31
35
  self.ip = ip
32
36
 
33
37
  def to_dict(self):
@@ -35,7 +39,29 @@ class KasadaPayloadInput:
35
39
  "userAgent": self.user_agent,
36
40
  "ipsLink": self.ips_link,
37
41
  "script": self.script,
38
- "acceptLanguage": self.acceptLanguage,
42
+ "acceptLanguage": self.accept_language,
39
43
  "ip": self.ip,
40
44
  }
41
45
  return result
46
+
47
+ class BotIDHeaderInput:
48
+ def __init__(self, script: str, user_agent: str, ip: str, accept_language: str):
49
+ # Script is the c.js script retrieved from the BotID script endpoint
50
+ self.script = script
51
+
52
+ # UserAgent must be a Chrome Windows User-Agent.
53
+ self.user_agent = user_agent
54
+
55
+ # IP is the IPV4 address of your network or proxy
56
+ self.ip = ip
57
+
58
+ # Your accept-language header
59
+ self.accept_language = accept_language
60
+
61
+ def to_dict(self):
62
+ return {
63
+ "script": self.script,
64
+ "userAgent": self.user_agent,
65
+ "ip": self.ip,
66
+ "acceptLanguage": self.accept_language,
67
+ }
@@ -1,14 +1,13 @@
1
1
  """Session class for Hyper Solutions API."""
2
2
 
3
3
  from typing import Optional, Dict, Any, Tuple
4
- from urllib.parse import quote
5
4
  import httpx
6
5
  import json
7
- import zstandard as zstd
6
+ import gzip
8
7
 
9
8
  from .shared import generate_signature, build_headers, validate_response
10
- from .akamai_input import SensorInput, PixelInput, DynamicInput, SbsdInput
11
- from .kasada_input import KasadaPowInput, KasadaPayloadInput
9
+ from .akamai_input import SensorInput, PixelInput, SbsdInput
10
+ from .kasada_input import KasadaPowInput, KasadaPayloadInput, BotIDHeaderInput
12
11
  from .datadome_input import DataDomeSliderInput, DataDomeInterstitialInput, DataDomeTagsInput
13
12
  from .incapsula_input import UtmvcInput, ReeseInput
14
13
  from .trustdecision_input import PayloadInput, DecodeInput, SignatureInput
@@ -24,12 +23,7 @@ class Session:
24
23
  self.app_secret = app_secret
25
24
  self.client = httpx.Client() if client is None else client
26
25
  self._owns_client = client is None
27
- self.compression = compression and zstd is not None
28
-
29
- # Initialize zstd compressor and decompressor if available
30
- if self.compression:
31
- self._compressor = zstd.ZstdCompressor(level=3)
32
- self._decompressor = zstd.ZstdDecompressor()
26
+ self.compression = compression
33
27
 
34
28
  def __enter__(self):
35
29
  return self
@@ -63,18 +57,18 @@ class Session:
63
57
  'bmsz': input_data.bmsz,
64
58
  'version': input_data.version,
65
59
  'pageUrl': input_data.page_url,
66
- 'scriptHash': input_data.script_hash,
67
- 'dynamicValues': input_data.dynamic_values,
60
+ 'script': input_data.script,
61
+ 'scriptUrl': input_data.script_url,
68
62
  'context': input_data.context,
69
63
  'ip': input_data.ip,
70
- 'acceptLanguage': input_data.acceptLanguage,
64
+ 'acceptLanguage': input_data.accept_language,
71
65
  }
72
66
  payload = json.dumps(payload_data).encode('utf-8')
73
67
 
74
68
  # Compress payload if large enough
75
69
  payload, use_compression = self._compress_payload(payload)
76
70
  if use_compression:
77
- headers["content-encoding"] = "zstd"
71
+ headers["content-encoding"] = "gzip"
78
72
 
79
73
  response = self.client.post(sensor_endpoint, headers=headers, content=payload)
80
74
 
@@ -102,26 +96,11 @@ class Session:
102
96
  'pageUrl': input_data.page_url,
103
97
  'o': input_data.o_cookie,
104
98
  'script': input_data.script,
105
- 'acceptLanguage': input_data.acceptLanguage,
99
+ 'acceptLanguage': input_data.accept_language,
106
100
  'ip': input_data.ip,
107
101
  'index': input_data.index,
108
102
  })
109
103
 
110
- def parse_v3_dynamic(self, input_data: DynamicInput) -> str:
111
- """
112
- Returns the dynamic values required to generate sensor data for V3 dynamic with Hyper Solutions API.
113
-
114
- Args:
115
- input_data (DynamicInput): An instance of DynamicInput containing the necessary data for parsing the script.
116
-
117
- Returns:
118
- str: Dynamic values as a string.
119
- """
120
- sensor_endpoint = "https://akm.hypersolutions.co/v3dynamic"
121
- return self._send_request(sensor_endpoint, {
122
- 'script': input_data.script,
123
- })
124
-
125
104
  def generate_pixel_data(self, input_data: PixelInput) -> str:
126
105
  """
127
106
  Returns the pixel data using the Hyper Solutions API.
@@ -138,17 +117,16 @@ class Session:
138
117
  'htmlVar': input_data.html_var,
139
118
  'scriptVar': input_data.script_var,
140
119
  'ip': input_data.ip,
141
- 'acceptLanguage': input_data.acceptLanguage,
120
+ 'acceptLanguage': input_data.accept_language,
142
121
  })
143
122
 
144
- def generate_reese84_sensor(self, site: str, input_data: ReeseInput) -> str:
123
+ def generate_reese84_sensor(self, input_data: ReeseInput) -> str:
145
124
  """
146
125
  Returns the sensor data required to generate valid reese84 cookies using the Hyper Solutions API.
147
126
 
148
127
  This function sends a request to the specified sensor endpoint with the necessary data to generate the reese84 sensor data.
149
128
 
150
129
  Args:
151
- site (str): The name of the site that will be used to generate the sensor data.
152
130
  input_data (ReeseInput): The input data.
153
131
 
154
132
  Returns:
@@ -157,11 +135,11 @@ class Session:
157
135
  Raises:
158
136
  ValueError: If the script attribute in input_data is empty.
159
137
  """
160
- return self._send_request("https://incapsula.hypersolutions.co/reese84/" + quote(site), {
138
+ return self._send_request("https://incapsula.hypersolutions.co/reese84", {
161
139
  'userAgent': input_data.user_agent,
162
- 'acceptLanguage': input_data.acceptLanguage,
140
+ 'acceptLanguage': input_data.accept_language,
163
141
  'ip': input_data.ip,
164
- 'scriptUrl': input_data.scriptUrl,
142
+ 'scriptUrl': input_data.script_url,
165
143
  'pageUrl': input_data.pageUrl,
166
144
  'pow': input_data.pow,
167
145
  'script': input_data.script,
@@ -195,7 +173,7 @@ class Session:
195
173
  # Compress payload if large enough
196
174
  payload, use_compression = self._compress_payload(payload)
197
175
  if use_compression:
198
- headers["content-encoding"] = "zstd"
176
+ headers["content-encoding"] = "gzip"
199
177
 
200
178
  response = self.client.post("https://incapsula.hypersolutions.co/utmvc", headers=headers, content=payload)
201
179
 
@@ -237,7 +215,7 @@ class Session:
237
215
  # Compress payload if large enough
238
216
  payload, use_compression = self._compress_payload(payload)
239
217
  if use_compression:
240
- headers["content-encoding"] = "zstd"
218
+ headers["content-encoding"] = "gzip"
241
219
 
242
220
  response = self.client.post("https://kasada.hypersolutions.co/payload", headers=headers, content=payload)
243
221
 
@@ -248,6 +226,19 @@ class Session:
248
226
 
249
227
  return response_data["payload"], response_data["headers"]
250
228
 
229
+ def generate_botid_header(self, input_data: BotIDHeaderInput) -> str:
230
+ """
231
+ Returns the x-is-human header value for Vercel BotID using the Hyper Solutions API.
232
+
233
+ Args:
234
+ input_data (BotIDHeaderInput): An instance of BotIDHeaderInput containing the script,
235
+ user agent, IP, and accept language.
236
+
237
+ Returns:
238
+ str: The x-is-human header value as a string.
239
+ """
240
+ return self._send_request("https://kasada.hypersolutions.co/botid", input_data.to_dict())
241
+
251
242
  def generate_interstitial_payload(self, input_data: DataDomeInterstitialInput) -> Dict[str, Any]:
252
243
  """
253
244
  Returns the DataDome interstitial payload value and response headers using the Hyper Solutions API.
@@ -316,7 +307,7 @@ class Session:
316
307
  # Compress payload if large enough
317
308
  payload, use_compression = self._compress_payload(payload)
318
309
  if use_compression:
319
- headers["content-encoding"] = "zstd"
310
+ headers["content-encoding"] = "gzip"
320
311
 
321
312
  response = self.client.post("https://trustdecision.hypersolutions.co/payload", headers=headers, content=payload)
322
313
 
@@ -382,12 +373,12 @@ class Session:
382
373
  headers = build_headers(self.api_key, self.jwt_key, self.app_key, self.app_secret)
383
374
  # Add compression headers
384
375
  if self.compression:
385
- headers["accept-encoding"] = "zstd"
376
+ headers["accept-encoding"] = "gzip"
386
377
  return headers
387
378
 
388
379
  def _compress_payload(self, payload: bytes) -> Tuple[bytes, bool]:
389
380
  """
390
- Compresses the payload using zstd if enabled and payload is large enough.
381
+ Compresses the payload using gzip if enabled and payload is large enough.
391
382
 
392
383
  Args:
393
384
  payload (bytes): The payload to potentially compress
@@ -399,7 +390,7 @@ class Session:
399
390
  return payload, False
400
391
 
401
392
  try:
402
- compressed = self._compressor.compress(payload)
393
+ compressed = gzip.compress(payload, compresslevel=6)
403
394
  return compressed, True
404
395
  except Exception:
405
396
  # Fall back to uncompressed if compression fails
@@ -407,7 +398,7 @@ class Session:
407
398
 
408
399
  def _decompress_response(self, response: httpx.Response) -> bytes:
409
400
  """
410
- Decompresses the response body if it's compressed with zstd.
401
+ Decompresses the response body if it's compressed with gzip.
411
402
 
412
403
  Args:
413
404
  response (httpx.Response): The HTTP response
@@ -418,9 +409,9 @@ class Session:
418
409
  content = response.content
419
410
  content_encoding = response.headers.get("content-encoding", "").lower()
420
411
 
421
- if content_encoding == "zstd" and self.compression:
412
+ if content_encoding == "gzip" and self.compression:
422
413
  try:
423
- return self._decompressor.decompress(content)
414
+ return gzip.decompress(content)
424
415
  except Exception:
425
416
  # Fall back to original content if decompression fails
426
417
  pass
@@ -444,7 +435,7 @@ class Session:
444
435
  # Compress payload if large enough
445
436
  payload, use_compression = self._compress_payload(payload)
446
437
  if use_compression:
447
- headers["content-encoding"] = "zstd"
438
+ headers["content-encoding"] = "gzip"
448
439
 
449
440
  response = self.client.post(url, headers=headers, content=payload)
450
441
 
@@ -471,7 +462,7 @@ class Session:
471
462
  # Compress payload if large enough
472
463
  payload, use_compression = self._compress_payload(payload)
473
464
  if use_compression:
474
- headers["content-encoding"] = "zstd"
465
+ headers["content-encoding"] = "gzip"
475
466
 
476
467
  response = self.client.post(url, headers=headers, content=payload)
477
468
 
@@ -1,14 +1,13 @@
1
1
  """Async version of the Session class for Hyper Solutions API."""
2
2
 
3
3
  from typing import Optional, Dict, Any, Tuple
4
- from urllib.parse import quote
5
4
  import httpx
6
5
  import json
7
- import zstandard as zstd
6
+ import gzip
8
7
 
9
8
  from .shared import generate_signature, build_headers, validate_response
10
- from .akamai_input import SensorInput, PixelInput, DynamicInput, SbsdInput
11
- from .kasada_input import KasadaPowInput, KasadaPayloadInput
9
+ from .akamai_input import SensorInput, PixelInput, SbsdInput
10
+ from .kasada_input import KasadaPowInput, KasadaPayloadInput, BotIDHeaderInput
12
11
  from .datadome_input import DataDomeSliderInput, DataDomeInterstitialInput, DataDomeTagsInput
13
12
  from .incapsula_input import UtmvcInput, ReeseInput
14
13
  from .trustdecision_input import PayloadInput, DecodeInput, SignatureInput
@@ -24,12 +23,7 @@ class SessionAsync:
24
23
  self.app_secret = app_secret
25
24
  self.client = client
26
25
  self._owns_client = client is None
27
- self.compression = compression and zstd is not None
28
-
29
- # Initialize zstd compressor and decompressor if available
30
- if self.compression:
31
- self._compressor = zstd.ZstdCompressor(level=3)
32
- self._decompressor = zstd.ZstdDecompressor()
26
+ self.compression = compression
33
27
 
34
28
  async def __aenter__(self):
35
29
  if self._owns_client:
@@ -67,18 +61,18 @@ class SessionAsync:
67
61
  'bmsz': input_data.bmsz,
68
62
  'version': input_data.version,
69
63
  'pageUrl': input_data.page_url,
70
- 'scriptHash': input_data.script_hash,
71
- 'dynamicValues': input_data.dynamic_values,
64
+ 'script': input_data.script,
65
+ 'scriptUrl': input_data.script_url,
72
66
  'context': input_data.context,
73
67
  'ip': input_data.ip,
74
- 'acceptLanguage': input_data.acceptLanguage,
68
+ 'acceptLanguage': input_data.accept_language,
75
69
  }
76
70
  payload = json.dumps(payload_data).encode('utf-8')
77
71
 
78
72
  # Compress payload if large enough
79
73
  payload, use_compression = self._compress_payload(payload)
80
74
  if use_compression:
81
- headers["content-encoding"] = "zstd"
75
+ headers["content-encoding"] = "gzip"
82
76
 
83
77
  response = await self.client.post(sensor_endpoint, headers=headers, content=payload)
84
78
 
@@ -106,26 +100,11 @@ class SessionAsync:
106
100
  'pageUrl': input_data.page_url,
107
101
  'o': input_data.o_cookie,
108
102
  'script': input_data.script,
109
- 'acceptLanguage': input_data.acceptLanguage,
103
+ 'acceptLanguage': input_data.accept_language,
110
104
  'ip': input_data.ip,
111
105
  'index': input_data.index,
112
106
  })
113
107
 
114
- async def parse_v3_dynamic(self, input_data: DynamicInput) -> str:
115
- """
116
- Returns the dynamic values required to generate sensor data for V3 dynamic with Hyper Solutions API.
117
-
118
- Args:
119
- input_data (DynamicInput): An instance of DynamicInput containing the necessary data for parsing the script.
120
-
121
- Returns:
122
- str: Dynamic values as a string.
123
- """
124
- sensor_endpoint = "https://akm.hypersolutions.co/v3dynamic"
125
- return await self._send_request(sensor_endpoint, {
126
- 'script': input_data.script,
127
- })
128
-
129
108
  async def generate_pixel_data(self, input_data: PixelInput) -> str:
130
109
  """
131
110
  Returns the pixel data using the Hyper Solutions API.
@@ -142,17 +121,16 @@ class SessionAsync:
142
121
  'htmlVar': input_data.html_var,
143
122
  'scriptVar': input_data.script_var,
144
123
  'ip': input_data.ip,
145
- 'acceptLanguage': input_data.acceptLanguage,
124
+ 'acceptLanguage': input_data.accept_language,
146
125
  })
147
126
 
148
- async def generate_reese84_sensor(self, site: str, input_data: ReeseInput) -> str:
127
+ async def generate_reese84_sensor(self, input_data: ReeseInput) -> str:
149
128
  """
150
129
  Returns the sensor data required to generate valid reese84 cookies using the Hyper Solutions API.
151
130
 
152
131
  This function sends a request to the specified sensor endpoint with the necessary data to generate the reese84 sensor data.
153
132
 
154
133
  Args:
155
- site (str): The name of the site that will be used to generate the sensor data.
156
134
  input_data (ReeseInput): The input data.
157
135
 
158
136
  Returns:
@@ -161,11 +139,11 @@ class SessionAsync:
161
139
  Raises:
162
140
  ValueError: If the script attribute in input_data is empty.
163
141
  """
164
- return await self._send_request("https://incapsula.hypersolutions.co/reese84/" + quote(site), {
142
+ return await self._send_request("https://incapsula.hypersolutions.co/reese84", {
165
143
  'userAgent': input_data.user_agent,
166
- 'acceptLanguage': input_data.acceptLanguage,
144
+ 'acceptLanguage': input_data.accept_language,
167
145
  'ip': input_data.ip,
168
- 'scriptUrl': input_data.scriptUrl,
146
+ 'scriptUrl': input_data.script_url,
169
147
  'pageUrl': input_data.pageUrl,
170
148
  'pow': input_data.pow,
171
149
  'script': input_data.script,
@@ -200,7 +178,7 @@ class SessionAsync:
200
178
  # Compress payload if large enough
201
179
  payload, use_compression = self._compress_payload(payload)
202
180
  if use_compression:
203
- headers["content-encoding"] = "zstd"
181
+ headers["content-encoding"] = "gzip"
204
182
 
205
183
  response = await self.client.post("https://incapsula.hypersolutions.co/utmvc", headers=headers, content=payload)
206
184
 
@@ -243,7 +221,7 @@ class SessionAsync:
243
221
  # Compress payload if large enough
244
222
  payload, use_compression = self._compress_payload(payload)
245
223
  if use_compression:
246
- headers["content-encoding"] = "zstd"
224
+ headers["content-encoding"] = "gzip"
247
225
 
248
226
  response = await self.client.post("https://kasada.hypersolutions.co/payload", headers=headers, content=payload)
249
227
 
@@ -254,6 +232,19 @@ class SessionAsync:
254
232
 
255
233
  return response_data["payload"], response_data["headers"]
256
234
 
235
+ async def generate_botid_header(self, input_data: BotIDHeaderInput) -> str:
236
+ """
237
+ Returns the x-is-human header value for Vercel BotID using the Hyper Solutions API.
238
+
239
+ Args:
240
+ input_data (BotIDHeaderInput): An instance of BotIDHeaderInput containing the script,
241
+ user agent, IP, and accept language.
242
+
243
+ Returns:
244
+ str: The x-is-human header value as a string.
245
+ """
246
+ return await self._send_request("https://kasada.hypersolutions.co/botid", input_data.to_dict())
247
+
257
248
  async def generate_interstitial_payload(self, input_data: DataDomeInterstitialInput) -> Dict[str, Any]:
258
249
  """
259
250
  Returns the DataDome interstitial payload value and response headers using the Hyper Solutions API.
@@ -325,7 +316,7 @@ class SessionAsync:
325
316
  # Compress payload if large enough
326
317
  payload, use_compression = self._compress_payload(payload)
327
318
  if use_compression:
328
- headers["content-encoding"] = "zstd"
319
+ headers["content-encoding"] = "gzip"
329
320
 
330
321
  response = await self.client.post("https://trustdecision.hypersolutions.co/payload", headers=headers, content=payload)
331
322
 
@@ -391,12 +382,12 @@ class SessionAsync:
391
382
  headers = build_headers(self.api_key, self.jwt_key, self.app_key, self.app_secret)
392
383
  # Add compression headers
393
384
  if self.compression:
394
- headers["accept-encoding"] = "zstd"
385
+ headers["accept-encoding"] = "gzip"
395
386
  return headers
396
387
 
397
388
  def _compress_payload(self, payload: bytes) -> Tuple[bytes, bool]:
398
389
  """
399
- Compresses the payload using zstd if enabled and payload is large enough.
390
+ Compresses the payload using gzip if enabled and payload is large enough.
400
391
 
401
392
  Args:
402
393
  payload (bytes): The payload to potentially compress
@@ -408,7 +399,7 @@ class SessionAsync:
408
399
  return payload, False
409
400
 
410
401
  try:
411
- compressed = self._compressor.compress(payload)
402
+ compressed = gzip.compress(payload, compresslevel=6)
412
403
  return compressed, True
413
404
  except Exception:
414
405
  # Fall back to uncompressed if compression fails
@@ -416,7 +407,7 @@ class SessionAsync:
416
407
 
417
408
  def _decompress_response(self, response: httpx.Response) -> bytes:
418
409
  """
419
- Decompresses the response body if it's compressed with zstd.
410
+ Decompresses the response body if it's compressed with gzip.
420
411
 
421
412
  Args:
422
413
  response (httpx.Response): The HTTP response
@@ -427,9 +418,9 @@ class SessionAsync:
427
418
  content = response.content
428
419
  content_encoding = response.headers.get("content-encoding", "").lower()
429
420
 
430
- if content_encoding == "zstd" and self.compression:
421
+ if content_encoding == "gzip" and self.compression:
431
422
  try:
432
- return self._decompressor.decompress(content)
423
+ return gzip.decompress(content)
433
424
  except Exception:
434
425
  # Fall back to original content if decompression fails
435
426
  pass
@@ -454,7 +445,7 @@ class SessionAsync:
454
445
  # Compress payload if large enough
455
446
  payload, use_compression = self._compress_payload(payload)
456
447
  if use_compression:
457
- headers["content-encoding"] = "zstd"
448
+ headers["content-encoding"] = "gzip"
458
449
 
459
450
  response = await self.client.post(url, headers=headers, content=payload)
460
451
 
@@ -482,7 +473,7 @@ class SessionAsync:
482
473
  # Compress payload if large enough
483
474
  payload, use_compression = self._compress_payload(payload)
484
475
  if use_compression:
485
- headers["content-encoding"] = "zstd"
476
+ headers["content-encoding"] = "gzip"
486
477
 
487
478
  response = await self.client.post(url, headers=headers, content=payload)
488
479
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyper_sdk
3
- Version: 2.7.0
3
+ Version: 2.12.1
4
4
  Summary: Hyper Solutions Python SDK
5
5
  License: MIT License
6
6
 
@@ -280,6 +280,31 @@ pow_payload = session.generate_kasada_pow(KasadaPowInput(
280
280
  ))
281
281
  ```
282
282
 
283
+ ## 🤖 Vercel BotID
284
+
285
+ Bypass **Vercel BotID** protection by generating the required `x-is-human` header.
286
+
287
+ ### Generating the x-is-human Header
288
+
289
+ Create the **x-is-human header** for Vercel BotID bypass:
290
+
291
+ ```python
292
+ from hyper_sdk import BotIDHeaderInput
293
+
294
+ header = session.generate_botid_header(BotIDHeaderInput(
295
+ script=script_body, # The c.js script content
296
+ user_agent="your-user-agent",
297
+ ip="your-proxy-ip",
298
+ accept_language="en-US,en;q=0.9",
299
+ ))
300
+
301
+ # Use the header in your requests
302
+ headers = {
303
+ "x-is-human": header,
304
+ # ... other headers
305
+ }
306
+ ```
307
+
283
308
  ### Script Path Extraction
284
309
 
285
310
  Extract **Kasada script paths** from blocked pages (HTTP 429):
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "hyper_sdk"
7
- version = "2.7.0"
7
+ version = "2.12.1"
8
8
  description = "Hyper Solutions Python SDK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.7"
@@ -21,7 +21,7 @@ dependencies = [
21
21
  "idna>=3.6",
22
22
  "jsonpickle>=3.0.3",
23
23
  "PyJWT>=2.8.0",
24
- "urllib3>=2.2.1",
24
+ "urllib3>=2.2.1"
25
25
  ]
26
26
  license = {file = "LICENSE"}
27
27
 
File without changes
File without changes