aioamazondevices 0.1.1__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.
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/PKG-INFO +1 -1
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/pyproject.toml +1 -1
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/src/aioamazondevices/__init__.py +1 -1
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/src/aioamazondevices/api.py +49 -27
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/LICENSE +0 -0
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/README.md +0 -0
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/src/aioamazondevices/const.py +0 -0
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/src/aioamazondevices/exceptions.py +0 -0
- {aioamazondevices-0.1.1 → aioamazondevices-0.3.0}/src/aioamazondevices/py.typed +0 -0
@@ -5,6 +5,7 @@ import hashlib
|
|
5
5
|
import secrets
|
6
6
|
import uuid
|
7
7
|
from dataclasses import dataclass
|
8
|
+
from pathlib import Path
|
8
9
|
from typing import Any
|
9
10
|
from urllib.parse import urlencode
|
10
11
|
|
@@ -39,27 +40,6 @@ class AmazonDevice:
|
|
39
40
|
wifi: str
|
40
41
|
|
41
42
|
|
42
|
-
def _build_init_cookies() -> dict[str, str]:
|
43
|
-
"""Build initial cookies to prevent captcha in most cases."""
|
44
|
-
token_bytes = secrets.token_bytes(313)
|
45
|
-
frc = base64.b64encode(token_bytes).decode("ascii").rstrip("=")
|
46
|
-
|
47
|
-
map_md_dict = {
|
48
|
-
"device_user_dictionary": [],
|
49
|
-
"device_registration_data": {
|
50
|
-
"software_version": AMAZON_SOFTWARE_VERSION,
|
51
|
-
},
|
52
|
-
"app_identifier": {
|
53
|
-
"app_version": AMAZON_APP_VERSION,
|
54
|
-
"bundle_id": AMAZON_APP_BUNDLE_ID,
|
55
|
-
},
|
56
|
-
}
|
57
|
-
map_md_str = orjson.dumps(map_md_dict).decode("utf-8")
|
58
|
-
map_md = base64.b64encode(map_md_str.encode()).decode().rstrip("=")
|
59
|
-
|
60
|
-
return {"frc": frc, "map-md": map_md, "amzn-app-id": AMAZON_APP_ID}
|
61
|
-
|
62
|
-
|
63
43
|
class AmazonEchoApi:
|
64
44
|
"""Queries Amazon for Echo devices."""
|
65
45
|
|
@@ -68,6 +48,7 @@ class AmazonEchoApi:
|
|
68
48
|
login_country_code: str,
|
69
49
|
login_email: str,
|
70
50
|
login_password: str,
|
51
|
+
save_html: bool = False,
|
71
52
|
) -> None:
|
72
53
|
"""Initialize the scanner."""
|
73
54
|
# Force country digits as lower case
|
@@ -85,11 +66,32 @@ class AmazonEchoApi:
|
|
85
66
|
self._login_password = login_password
|
86
67
|
self._domain = domain
|
87
68
|
self._url = f"https://www.amazon.{domain}"
|
88
|
-
self._cookies = _build_init_cookies()
|
69
|
+
self._cookies = self._build_init_cookies()
|
89
70
|
self._headers = DEFAULT_HEADERS
|
71
|
+
self._save_html = save_html
|
90
72
|
|
91
73
|
self.session: AsyncClient
|
92
74
|
|
75
|
+
def _build_init_cookies(self) -> dict[str, str]:
|
76
|
+
"""Build initial cookies to prevent captcha in most cases."""
|
77
|
+
token_bytes = secrets.token_bytes(313)
|
78
|
+
frc = base64.b64encode(token_bytes).decode("ascii").rstrip("=")
|
79
|
+
|
80
|
+
map_md_dict = {
|
81
|
+
"device_user_dictionary": [],
|
82
|
+
"device_registration_data": {
|
83
|
+
"software_version": AMAZON_SOFTWARE_VERSION,
|
84
|
+
},
|
85
|
+
"app_identifier": {
|
86
|
+
"app_version": AMAZON_APP_VERSION,
|
87
|
+
"bundle_id": AMAZON_APP_BUNDLE_ID,
|
88
|
+
},
|
89
|
+
}
|
90
|
+
map_md_str = orjson.dumps(map_md_dict).decode("utf-8")
|
91
|
+
map_md = base64.b64encode(map_md_str.encode()).decode().rstrip("=")
|
92
|
+
|
93
|
+
return {"frc": frc, "map-md": map_md, "amzn-app-id": AMAZON_APP_ID}
|
94
|
+
|
93
95
|
def _create_code_verifier(self, length: int = 32) -> bytes:
|
94
96
|
"""Create code verifier."""
|
95
97
|
verifier = secrets.token_bytes(length)
|
@@ -100,18 +102,18 @@ class AmazonEchoApi:
|
|
100
102
|
m = hashlib.sha256(verifier)
|
101
103
|
return base64.urlsafe_b64encode(m.digest()).rstrip(b"=")
|
102
104
|
|
103
|
-
def _build_client_id(self) -> str:
|
105
|
+
def _build_client_id(self, serial: str) -> str:
|
104
106
|
"""Build client ID."""
|
105
|
-
serial = uuid.uuid4().hex.upper()
|
106
107
|
client_id = serial.encode() + AMAZON_SERIAL_NUMBER
|
107
108
|
return client_id.hex()
|
108
109
|
|
109
110
|
def _build_oauth_url(
|
110
111
|
self,
|
112
|
+
code_verifier: bytes,
|
113
|
+
client_id: str,
|
111
114
|
) -> str:
|
112
115
|
"""Build the url to login to Amazon as a Mobile device."""
|
113
|
-
|
114
|
-
code_challenge = self._create_s256_code_challenge(self._create_code_verifier())
|
116
|
+
code_challenge = self._create_s256_code_challenge(code_verifier)
|
115
117
|
|
116
118
|
oauth_params = {
|
117
119
|
"openid.oa2.response_type": "code",
|
@@ -183,15 +185,34 @@ class AmazonEchoApi:
|
|
183
185
|
url,
|
184
186
|
data=input_data,
|
185
187
|
)
|
188
|
+
|
189
|
+
await self._save_to_file(
|
190
|
+
resp.text,
|
191
|
+
url,
|
192
|
+
)
|
186
193
|
return BeautifulSoup(resp.content, "html.parser"), resp
|
187
194
|
|
195
|
+
async def _save_to_file(self, html_code: str, url: str) -> None:
|
196
|
+
"""Sage HTML data to disk."""
|
197
|
+
if not self._save_html:
|
198
|
+
return
|
199
|
+
|
200
|
+
url_split = url.split("/")
|
201
|
+
filename = f"{url_split[3]}-{url_split[4].split('?')[0]}.html"
|
202
|
+
with Path.open(Path(filename), "w+") as file:
|
203
|
+
file.write(html_code)
|
204
|
+
file.write("\n")
|
205
|
+
|
188
206
|
async def login(self, otp_code: str) -> bool:
|
189
207
|
"""Login to Amazon."""
|
190
208
|
_LOGGER.debug("Logging-in for %s [otp code %s]", self._login_email, otp_code)
|
191
209
|
self._client_session()
|
192
210
|
|
211
|
+
code_verifier = self._create_code_verifier()
|
212
|
+
client_id = self._build_client_id(uuid.uuid4().hex.upper())
|
213
|
+
|
193
214
|
_LOGGER.debug("Build oauth URL")
|
194
|
-
login_url = self._build_oauth_url()
|
215
|
+
login_url = self._build_oauth_url(code_verifier, client_id)
|
195
216
|
|
196
217
|
login_soup, _ = await self._session_request("GET", login_url)
|
197
218
|
login_method, login_url = self._get_request_from_soup(login_soup)
|
@@ -199,6 +220,7 @@ class AmazonEchoApi:
|
|
199
220
|
login_inputs["email"] = self._login_email
|
200
221
|
login_inputs["password"] = self._login_password
|
201
222
|
|
223
|
+
_LOGGER.debug("Register at %s", login_url)
|
202
224
|
login_soup, _ = await self._session_request(
|
203
225
|
login_method,
|
204
226
|
login_url,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|