roolink 1.0.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.
roolink-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Roolink
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
roolink-1.0.0/PKG-INFO ADDED
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: roolink
3
+ Version: 1.0.0
4
+ Summary: Official Roolink SDK for Python
5
+ Author: Roolink
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/roolinkio/roolink-py
8
+ Project-URL: Documentation, https://docs.roolink.io
9
+ Project-URL: Source Code, https://github.com/roolinkio/roolink-py
10
+ Project-URL: Bug Tracker, https://github.com/roolinkio/roolink-py/issues
11
+ Keywords: roolink,akamai,sensor,sdk
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: httpx[http2]>=0.27.0
25
+ Dynamic: license-file
26
+
27
+ <div align="center">
28
+
29
+ # 🔗 Roolink Python SDK
30
+
31
+ ### Enterprise-grade Akamai sensor generation for Python
32
+
33
+ [![PyPI](https://img.shields.io/badge/PyPI-1.0.0-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://pypi.org/project/roolink/)
34
+ [![Python](https://img.shields.io/badge/Python-3.8+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
35
+ [![License](https://img.shields.io/badge/License-MIT-purple?style=for-the-badge)](LICENSE)
36
+
37
+ **[📚 Documentation](https://docs.roolink.io/)** • **[💬 Discord](https://discord.gg/rooapi)**
38
+
39
+ ---
40
+
41
+ </div>
42
+
43
+ ## 🚀 Overview
44
+
45
+ Production-ready APIs for generating Akamai sensor data across web and mobile platforms. This SDK handles authentication, requests, and response parsing with full type hints.
46
+
47
+ ### 🎯 Supported Services
48
+
49
+ | Service | Features |
50
+ |---------|----------|
51
+ | **🌐 Web API** | Sensor generation, pixel challenges, SBSD, sec-cpt solving |
52
+ | **📱 BMP API** | iOS & Android mobile app sensor generation |
53
+
54
+ ---
55
+
56
+ ## 📦 Installation
57
+
58
+ ```bash
59
+ pip install roolink
60
+ ```
61
+
62
+ ## ⚡ Quick Start
63
+
64
+ ```python
65
+ from roolink import RoolinkClient
66
+
67
+ client = RoolinkClient('your-api-key')
68
+ sensor = client.generate_web_sensor(req)
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 🌐 Web API
74
+
75
+ ```python
76
+ # Sensor Generation
77
+ client.generate_web_sensor(req)
78
+
79
+ # Pixel Challenges
80
+ client.generate_pixel(req)
81
+
82
+ # Sec-Cpt Challenges
83
+ client.solve_sec_cpt(req)
84
+
85
+ # SBSD Challenges
86
+ client.solve_sbsd(req)
87
+
88
+ # Script Parsing
89
+ client.parse_script(script_content)
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 📱 BMP API
95
+
96
+ ```python
97
+ # iOS Sensors (android=False)
98
+ client.generate_bmp_sensor(BMPSensorRequest(
99
+ android=False,
100
+ ))
101
+
102
+ # Android Sensors (android=True)
103
+ client.generate_bmp_sensor(BMPSensorRequest(
104
+ android=True,
105
+ ))
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 🆘 Error Handling
111
+
112
+ ```python
113
+ try:
114
+ result = client.generate_web_sensor(req)
115
+ except Exception as e:
116
+ # handle error
117
+ pass
118
+ ```
119
+
120
+ ---
121
+
122
+ ## 📄 License
123
+
124
+ MIT License - see [LICENSE](LICENSE) for details.
125
+
126
+ ---
127
+
128
+ <div align="center">
129
+
130
+ **Built with 💜**
131
+
132
+ </div>
@@ -0,0 +1,106 @@
1
+ <div align="center">
2
+
3
+ # 🔗 Roolink Python SDK
4
+
5
+ ### Enterprise-grade Akamai sensor generation for Python
6
+
7
+ [![PyPI](https://img.shields.io/badge/PyPI-1.0.0-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://pypi.org/project/roolink/)
8
+ [![Python](https://img.shields.io/badge/Python-3.8+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
9
+ [![License](https://img.shields.io/badge/License-MIT-purple?style=for-the-badge)](LICENSE)
10
+
11
+ **[📚 Documentation](https://docs.roolink.io/)** • **[💬 Discord](https://discord.gg/rooapi)**
12
+
13
+ ---
14
+
15
+ </div>
16
+
17
+ ## 🚀 Overview
18
+
19
+ Production-ready APIs for generating Akamai sensor data across web and mobile platforms. This SDK handles authentication, requests, and response parsing with full type hints.
20
+
21
+ ### 🎯 Supported Services
22
+
23
+ | Service | Features |
24
+ |---------|----------|
25
+ | **🌐 Web API** | Sensor generation, pixel challenges, SBSD, sec-cpt solving |
26
+ | **📱 BMP API** | iOS & Android mobile app sensor generation |
27
+
28
+ ---
29
+
30
+ ## 📦 Installation
31
+
32
+ ```bash
33
+ pip install roolink
34
+ ```
35
+
36
+ ## ⚡ Quick Start
37
+
38
+ ```python
39
+ from roolink import RoolinkClient
40
+
41
+ client = RoolinkClient('your-api-key')
42
+ sensor = client.generate_web_sensor(req)
43
+ ```
44
+
45
+ ---
46
+
47
+ ## 🌐 Web API
48
+
49
+ ```python
50
+ # Sensor Generation
51
+ client.generate_web_sensor(req)
52
+
53
+ # Pixel Challenges
54
+ client.generate_pixel(req)
55
+
56
+ # Sec-Cpt Challenges
57
+ client.solve_sec_cpt(req)
58
+
59
+ # SBSD Challenges
60
+ client.solve_sbsd(req)
61
+
62
+ # Script Parsing
63
+ client.parse_script(script_content)
64
+ ```
65
+
66
+ ---
67
+
68
+ ## 📱 BMP API
69
+
70
+ ```python
71
+ # iOS Sensors (android=False)
72
+ client.generate_bmp_sensor(BMPSensorRequest(
73
+ android=False,
74
+ ))
75
+
76
+ # Android Sensors (android=True)
77
+ client.generate_bmp_sensor(BMPSensorRequest(
78
+ android=True,
79
+ ))
80
+ ```
81
+
82
+ ---
83
+
84
+ ## 🆘 Error Handling
85
+
86
+ ```python
87
+ try:
88
+ result = client.generate_web_sensor(req)
89
+ except Exception as e:
90
+ # handle error
91
+ pass
92
+ ```
93
+
94
+ ---
95
+
96
+ ## 📄 License
97
+
98
+ MIT License - see [LICENSE](LICENSE) for details.
99
+
100
+ ---
101
+
102
+ <div align="center">
103
+
104
+ **Built with 💜**
105
+
106
+ </div>
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "roolink"
7
+ version = "1.0.0"
8
+ description = "Official Roolink SDK for Python"
9
+ readme = "README.md"
10
+ requires-python = ">=3.8"
11
+ license = {text = "MIT"}
12
+ keywords = ["roolink", "akamai", "sensor", "sdk"]
13
+ authors = [
14
+ {name = "Roolink"}
15
+ ]
16
+ classifiers = [
17
+ "Development Status :: 5 - Production/Stable",
18
+ "Intended Audience :: Developers",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.8",
22
+ "Programming Language :: Python :: 3.9",
23
+ "Programming Language :: Python :: 3.10",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ ]
27
+ dependencies = [
28
+ "httpx[http2]>=0.27.0",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://github.com/roolinkio/roolink-py"
33
+ Documentation = "https://docs.roolink.io"
34
+ "Source Code" = "https://github.com/roolinkio/roolink-py"
35
+ "Bug Tracker" = "https://github.com/roolinkio/roolink-py/issues"
@@ -0,0 +1,31 @@
1
+ from .client import RoolinkClient
2
+ from .types.bmp import BMPSensorRequest, BMPSensorResponse, Cookie
3
+ from .types.web import (
4
+ WebSensorRequest,
5
+ WebSensorResponse,
6
+ PixelRequest,
7
+ PixelResponse,
8
+ SecCptRequest,
9
+ SecCptResponse,
10
+ SBSDRequest,
11
+ SBSDResponse,
12
+ ParseResponse,
13
+ )
14
+
15
+ __version__ = "1.0.0"
16
+
17
+ __all__ = [
18
+ "RoolinkClient",
19
+ "BMPSensorRequest",
20
+ "BMPSensorResponse",
21
+ "Cookie",
22
+ "WebSensorRequest",
23
+ "WebSensorResponse",
24
+ "PixelRequest",
25
+ "PixelResponse",
26
+ "SecCptRequest",
27
+ "SecCptResponse",
28
+ "SBSDRequest",
29
+ "SBSDResponse",
30
+ "ParseResponse",
31
+ ]
@@ -0,0 +1,99 @@
1
+ from typing import Optional, Any, Dict, Union
2
+ import httpx
3
+
4
+ from .types.bmp import BMPSensorRequest, BMPSensorResponse
5
+ from .types.web import (
6
+ WebSensorRequest,
7
+ WebSensorResponse,
8
+ PixelRequest,
9
+ PixelResponse,
10
+ SecCptRequest,
11
+ SecCptResponse,
12
+ SBSDRequest,
13
+ SBSDResponse,
14
+ ParseResponse,
15
+ )
16
+
17
+ BMP_BASE_URL = "https://bmp.roolink.io"
18
+ WEB_BASE_URL = "https://web.roolink.io"
19
+ DEFAULT_TIMEOUT = 30.0
20
+
21
+
22
+ class RoolinkClient:
23
+ """Client for interacting with Roolink services."""
24
+
25
+ def __init__(self, api_key: str):
26
+ """
27
+ Initialize the Roolink client.
28
+
29
+ Args:
30
+ api_key: Your Roolink API key
31
+ """
32
+ self.api_key = api_key
33
+ self.timeout = DEFAULT_TIMEOUT
34
+
35
+ self.client = httpx.Client(
36
+ http2=True,
37
+ timeout=self.timeout,
38
+ headers={
39
+ "x-api-key": self.api_key,
40
+ "Content-Type": "application/json",
41
+ "Accept-Encoding": "gzip, deflate, br",
42
+ },
43
+ )
44
+
45
+ def _request(self, method: str, url: str, data: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
46
+ """Make an HTTP request."""
47
+ response = self.client.request(
48
+ method=method,
49
+ url=url,
50
+ json=data,
51
+ )
52
+
53
+ if response.status_code >= 400:
54
+ raise Exception(f"API error (status {response.status_code}): {response.text}")
55
+
56
+ return response.json()
57
+
58
+ def __del__(self):
59
+ """Clean up the HTTP client."""
60
+ if hasattr(self, 'client'):
61
+ self.client.close()
62
+
63
+ # BMP API Methods
64
+ def generate_bmp_sensor(self, req: BMPSensorRequest) -> BMPSensorResponse:
65
+ """Generate an Akamai BMP sensor for mobile apps."""
66
+ url = f"{BMP_BASE_URL}/api/v1/sensor"
67
+ data = self._request("POST", url, req.to_dict())
68
+ return BMPSensorResponse.from_dict(data)
69
+
70
+ # Web API Methods
71
+ def generate_web_sensor(self, req: WebSensorRequest) -> WebSensorResponse:
72
+ """Generate an Akamai web sensor."""
73
+ url = f"{WEB_BASE_URL}/api/v1/sensor"
74
+ data = self._request("POST", url, req.to_dict())
75
+ return WebSensorResponse.from_dict(data)
76
+
77
+ def generate_pixel(self, req: PixelRequest) -> PixelResponse:
78
+ """Generate pixel sensor data."""
79
+ url = f"{WEB_BASE_URL}/api/v1/pixel"
80
+ data = self._request("POST", url, req.to_dict())
81
+ return PixelResponse.from_dict(data)
82
+
83
+ def solve_sec_cpt(self, req: SecCptRequest) -> SecCptResponse:
84
+ """Solve a sec-cpt crypto challenge."""
85
+ url = f"{WEB_BASE_URL}/api/v1/sec-cpt"
86
+ data = self._request("POST", url, req.to_dict())
87
+ return SecCptResponse.from_dict(data)
88
+
89
+ def solve_sbsd(self, req: SBSDRequest) -> SBSDResponse:
90
+ """Solve an SBSD challenge."""
91
+ url = f"{WEB_BASE_URL}/api/v1/sbsd"
92
+ data = self._request("POST", url, req.to_dict())
93
+ return SBSDResponse.from_dict(data)
94
+
95
+ def parse_script(self, script_content: str) -> ParseResponse:
96
+ """Parse an Akamai script and return script data."""
97
+ url = f"{WEB_BASE_URL}/api/v1/parse"
98
+ data = self._request("POST", url, {"scriptContent": script_content})
99
+ return ParseResponse.from_dict(data)
@@ -0,0 +1 @@
1
+ """Type definitions for Roolink SDK."""
@@ -0,0 +1,85 @@
1
+ from typing import Optional, List, Dict, Any
2
+ from dataclasses import dataclass, field, asdict
3
+
4
+
5
+ @dataclass
6
+ class BMPSensorRequest:
7
+ """Request to generate an Akamai BMP sensor."""
8
+
9
+ app: str
10
+ proxy: str
11
+ language: Optional[str] = None
12
+ android: Optional[bool] = None
13
+ ipad: Optional[bool] = None
14
+
15
+ def to_dict(self) -> Dict[str, Any]:
16
+ """Convert to dictionary, excluding None values."""
17
+ return {k: v for k, v in asdict(self).items() if v is not None}
18
+
19
+
20
+ @dataclass
21
+ class Cookie:
22
+ """Cookie returned from BMP sensor generation."""
23
+
24
+ name: str
25
+ value: str
26
+ domain: str
27
+
28
+ @classmethod
29
+ def from_dict(cls, data: Dict[str, Any]) -> "Cookie":
30
+ return cls(
31
+ name=data["name"],
32
+ value=data["value"],
33
+ domain=data["domain"],
34
+ )
35
+
36
+
37
+ @dataclass
38
+ class BMPSensorResponse:
39
+ """Response from BMP sensor generation."""
40
+
41
+ sensor: str
42
+ platform: str
43
+ device_id: str
44
+ app_version: str
45
+ screen_height: int
46
+ screen_width: int
47
+ language: str
48
+
49
+ # iOS specific fields
50
+ ios: Optional[str] = None
51
+ kernel_os_release: Optional[str] = None
52
+ kernel_os_version: Optional[str] = None
53
+ machine_id: Optional[str] = None
54
+ cookies: Optional[List[Cookie]] = None
55
+
56
+ # Android specific fields
57
+ android: Optional[str] = None
58
+ device_model: Optional[str] = None
59
+ device_manufacturer: Optional[str] = None
60
+ sdk_version: Optional[str] = None
61
+
62
+ @classmethod
63
+ def from_dict(cls, data: Dict[str, Any]) -> "BMPSensorResponse":
64
+ cookies = None
65
+ if "cookies" in data and data["cookies"]:
66
+ cookies = [Cookie.from_dict(c) for c in data["cookies"]]
67
+
68
+ return cls(
69
+ sensor=data["sensor"],
70
+ platform=data["platform"],
71
+ device_id=data["deviceId"],
72
+ app_version=data["appVersion"],
73
+ screen_height=data["screenHeight"],
74
+ screen_width=data["screenWidth"],
75
+ language=data["language"],
76
+ ios=data.get("ios"),
77
+ kernel_os_release=data.get("kernelOsRelease"),
78
+ kernel_os_version=data.get("kernelOsVersion"),
79
+ machine_id=data.get("machineId"),
80
+ cookies=cookies,
81
+ android=data.get("android"),
82
+ device_model=data.get("deviceModel"),
83
+ device_manufacturer=data.get("deviceManufacturer"),
84
+ sdk_version=data.get("sdkVersion"),
85
+ )
@@ -0,0 +1,196 @@
1
+ from typing import Optional, Dict, Any, List
2
+ from dataclasses import dataclass, asdict
3
+
4
+ # Type alias for parsed Akamai script configuration data
5
+ ScriptData = Dict[str, Any]
6
+
7
+
8
+ @dataclass
9
+ class WebSensorRequest:
10
+ """Request to generate an Akamai web sensor."""
11
+
12
+ user_agent: str
13
+ url: str
14
+ flags: Optional[str] = None
15
+ abck: Optional[str] = None
16
+ bm_sz: Optional[str] = None
17
+ sec_cpt: Optional[bool] = None
18
+ index: Optional[int] = None
19
+ stepper: Optional[bool] = None
20
+ keyboard: Optional[bool] = None
21
+ count: Optional[bool] = None
22
+ language: Optional[str] = None
23
+ custom_sensor: Optional[bool] = None
24
+ script_data: Optional[ScriptData] = None
25
+ script_url: Optional[str] = None
26
+
27
+ def to_dict(self) -> Dict[str, Any]:
28
+ """Convert to dictionary with correct JSON field names."""
29
+ result = {
30
+ "userAgent": self.user_agent,
31
+ "url": self.url,
32
+ }
33
+ if self.flags:
34
+ result["flags"] = self.flags
35
+ if self.abck:
36
+ result["_abck"] = self.abck
37
+ if self.bm_sz:
38
+ result["bm_sz"] = self.bm_sz
39
+ if self.sec_cpt is not None:
40
+ result["sec_cpt"] = self.sec_cpt
41
+ if self.index is not None:
42
+ result["index"] = self.index
43
+ if self.stepper is not None:
44
+ result["stepper"] = self.stepper
45
+ if self.keyboard is not None:
46
+ result["keyboard"] = self.keyboard
47
+ if self.count is not None:
48
+ result["count"] = self.count
49
+ if self.language:
50
+ result["language"] = self.language
51
+ if self.custom_sensor is not None:
52
+ result["customSensor"] = self.custom_sensor
53
+ if self.script_data:
54
+ result["scriptData"] = self.script_data
55
+ if self.script_url:
56
+ result["scriptUrl"] = self.script_url
57
+ return result
58
+
59
+
60
+ @dataclass
61
+ class WebSensorResponse:
62
+ """Response from web sensor generation."""
63
+
64
+ sensor: str
65
+
66
+ @classmethod
67
+ def from_dict(cls, data: Dict[str, Any]) -> "WebSensorResponse":
68
+ return cls(sensor=data["sensor"])
69
+
70
+
71
+ @dataclass
72
+ class PixelRequest:
73
+ """Request to generate pixel data."""
74
+
75
+ user_agent: str
76
+ bazadebezolkohpepadr: int
77
+ hash: str
78
+
79
+ def to_dict(self) -> Dict[str, Any]:
80
+ return {
81
+ "userAgent": self.user_agent,
82
+ "bazadebezolkohpepadr": self.bazadebezolkohpepadr,
83
+ "hash": self.hash,
84
+ }
85
+
86
+
87
+ @dataclass
88
+ class PixelResponse:
89
+ """Response from pixel generation."""
90
+
91
+ sensor: str
92
+
93
+ @classmethod
94
+ def from_dict(cls, data: Dict[str, Any]) -> "PixelResponse":
95
+ return cls(sensor=data["sensor"])
96
+
97
+
98
+ @dataclass
99
+ class SecCptRequest:
100
+ """Request to solve a sec-cpt challenge."""
101
+
102
+ token: str
103
+ timestamp: int
104
+ nonce: str
105
+ difficulty: int
106
+ cookie: str
107
+ sec_cp_challenge: Optional[str] = None
108
+ provider: Optional[str] = None
109
+ branding_url_content: Optional[str] = None
110
+ chlg_duration: Optional[int] = None
111
+ timeout: Optional[int] = None
112
+ cpu: Optional[bool] = None
113
+
114
+ def to_dict(self) -> Dict[str, Any]:
115
+ result = {
116
+ "token": self.token,
117
+ "timestamp": self.timestamp,
118
+ "nonce": self.nonce,
119
+ "difficulty": self.difficulty,
120
+ "cookie": self.cookie,
121
+ }
122
+ if self.sec_cp_challenge:
123
+ result["sec-cp-challenge"] = self.sec_cp_challenge
124
+ if self.provider:
125
+ result["provider"] = self.provider
126
+ if self.branding_url_content:
127
+ result["branding_url_content"] = self.branding_url_content
128
+ if self.chlg_duration is not None:
129
+ result["chlg_duration"] = self.chlg_duration
130
+ if self.timeout is not None:
131
+ result["timeout"] = self.timeout
132
+ if self.cpu is not None:
133
+ result["cpu"] = self.cpu
134
+ return result
135
+
136
+
137
+ @dataclass
138
+ class SecCptResponse:
139
+ """Response from sec-cpt challenge."""
140
+
141
+ token: str
142
+ answers: List[str]
143
+
144
+ @classmethod
145
+ def from_dict(cls, data: Dict[str, Any]) -> "SecCptResponse":
146
+ return cls(token=data["token"], answers=data["answers"])
147
+
148
+
149
+ @dataclass
150
+ class SBSDRequest:
151
+ """Request to solve SBSD challenge."""
152
+
153
+ vid: str
154
+ user_agent: str
155
+ bm_o: str
156
+ url: str
157
+ legacy: Optional[bool] = None
158
+ script_hash: Optional[str] = None
159
+ script_url: Optional[str] = None
160
+
161
+ def to_dict(self) -> Dict[str, Any]:
162
+ result = {
163
+ "vid": self.vid,
164
+ "userAgent": self.user_agent,
165
+ "bm_o": self.bm_o,
166
+ "url": self.url,
167
+ }
168
+ if self.legacy is not None:
169
+ result["legacy"] = self.legacy
170
+ if self.script_hash:
171
+ result["script_hash"] = self.script_hash
172
+ if self.script_url:
173
+ result["script_url"] = self.script_url
174
+ return result
175
+
176
+
177
+ @dataclass
178
+ class SBSDResponse:
179
+ """Response from SBSD challenge."""
180
+
181
+ body: str
182
+
183
+ @classmethod
184
+ def from_dict(cls, data: Dict[str, Any]) -> "SBSDResponse":
185
+ return cls(body=data["body"])
186
+
187
+
188
+ @dataclass
189
+ class ParseResponse:
190
+ """Response from script parsing."""
191
+
192
+ script_data: Dict[str, Any]
193
+
194
+ @classmethod
195
+ def from_dict(cls, data: Dict[str, Any]) -> "ParseResponse":
196
+ return cls(script_data=data["scriptData"])
@@ -0,0 +1,132 @@
1
+ Metadata-Version: 2.4
2
+ Name: roolink
3
+ Version: 1.0.0
4
+ Summary: Official Roolink SDK for Python
5
+ Author: Roolink
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/roolinkio/roolink-py
8
+ Project-URL: Documentation, https://docs.roolink.io
9
+ Project-URL: Source Code, https://github.com/roolinkio/roolink-py
10
+ Project-URL: Bug Tracker, https://github.com/roolinkio/roolink-py/issues
11
+ Keywords: roolink,akamai,sensor,sdk
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.8
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Requires-Python: >=3.8
22
+ Description-Content-Type: text/markdown
23
+ License-File: LICENSE
24
+ Requires-Dist: httpx[http2]>=0.27.0
25
+ Dynamic: license-file
26
+
27
+ <div align="center">
28
+
29
+ # 🔗 Roolink Python SDK
30
+
31
+ ### Enterprise-grade Akamai sensor generation for Python
32
+
33
+ [![PyPI](https://img.shields.io/badge/PyPI-1.0.0-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://pypi.org/project/roolink/)
34
+ [![Python](https://img.shields.io/badge/Python-3.8+-3776AB?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org/)
35
+ [![License](https://img.shields.io/badge/License-MIT-purple?style=for-the-badge)](LICENSE)
36
+
37
+ **[📚 Documentation](https://docs.roolink.io/)** • **[💬 Discord](https://discord.gg/rooapi)**
38
+
39
+ ---
40
+
41
+ </div>
42
+
43
+ ## 🚀 Overview
44
+
45
+ Production-ready APIs for generating Akamai sensor data across web and mobile platforms. This SDK handles authentication, requests, and response parsing with full type hints.
46
+
47
+ ### 🎯 Supported Services
48
+
49
+ | Service | Features |
50
+ |---------|----------|
51
+ | **🌐 Web API** | Sensor generation, pixel challenges, SBSD, sec-cpt solving |
52
+ | **📱 BMP API** | iOS & Android mobile app sensor generation |
53
+
54
+ ---
55
+
56
+ ## 📦 Installation
57
+
58
+ ```bash
59
+ pip install roolink
60
+ ```
61
+
62
+ ## ⚡ Quick Start
63
+
64
+ ```python
65
+ from roolink import RoolinkClient
66
+
67
+ client = RoolinkClient('your-api-key')
68
+ sensor = client.generate_web_sensor(req)
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 🌐 Web API
74
+
75
+ ```python
76
+ # Sensor Generation
77
+ client.generate_web_sensor(req)
78
+
79
+ # Pixel Challenges
80
+ client.generate_pixel(req)
81
+
82
+ # Sec-Cpt Challenges
83
+ client.solve_sec_cpt(req)
84
+
85
+ # SBSD Challenges
86
+ client.solve_sbsd(req)
87
+
88
+ # Script Parsing
89
+ client.parse_script(script_content)
90
+ ```
91
+
92
+ ---
93
+
94
+ ## 📱 BMP API
95
+
96
+ ```python
97
+ # iOS Sensors (android=False)
98
+ client.generate_bmp_sensor(BMPSensorRequest(
99
+ android=False,
100
+ ))
101
+
102
+ # Android Sensors (android=True)
103
+ client.generate_bmp_sensor(BMPSensorRequest(
104
+ android=True,
105
+ ))
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 🆘 Error Handling
111
+
112
+ ```python
113
+ try:
114
+ result = client.generate_web_sensor(req)
115
+ except Exception as e:
116
+ # handle error
117
+ pass
118
+ ```
119
+
120
+ ---
121
+
122
+ ## 📄 License
123
+
124
+ MIT License - see [LICENSE](LICENSE) for details.
125
+
126
+ ---
127
+
128
+ <div align="center">
129
+
130
+ **Built with 💜**
131
+
132
+ </div>
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ roolink/__init__.py
5
+ roolink/client.py
6
+ roolink.egg-info/PKG-INFO
7
+ roolink.egg-info/SOURCES.txt
8
+ roolink.egg-info/dependency_links.txt
9
+ roolink.egg-info/requires.txt
10
+ roolink.egg-info/top_level.txt
11
+ roolink/types/__init__.py
12
+ roolink/types/bmp.py
13
+ roolink/types/web.py
@@ -0,0 +1 @@
1
+ httpx[http2]>=0.27.0
@@ -0,0 +1 @@
1
+ roolink
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+