AlexaPy 1.29.24__tar.gz → 1.29.25__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.
- {alexapy-1.29.24 → alexapy-1.29.25}/PKG-INFO +1 -1
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/alexaapi.py +16 -2
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/alexalogin.py +47 -20
- {alexapy-1.29.24 → alexapy-1.29.25}/pyproject.toml +1 -1
- {alexapy-1.29.24 → alexapy-1.29.25}/AUTHORS.md +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/LICENSE +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/README.md +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/__init__.py +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/alexahttp2.py +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/alexaproxy.py +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/alexawebsocket.py +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/const.py +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/errors.py +0 -0
- {alexapy-1.29.24 → alexapy-1.29.25}/alexapy/helpers.py +0 -0
|
@@ -1475,9 +1475,23 @@ class AlexaAPI:
|
|
|
1475
1475
|
"get", login, "/api/devices-v2/device", query=None
|
|
1476
1476
|
)
|
|
1477
1477
|
devices, *_ = await get_json_value(response, "devices", list)
|
|
1478
|
-
|
|
1479
|
-
|
|
1478
|
+
|
|
1479
|
+
if devices is not None:
|
|
1480
|
+
AlexaAPI.devices[login.email] = devices
|
|
1481
|
+
return devices
|
|
1482
|
+
|
|
1483
|
+
if login.email in AlexaAPI.devices:
|
|
1484
|
+
_LOGGER.debug(
|
|
1485
|
+
"%s: Using cached device list because device API returned no data",
|
|
1486
|
+
hide_email(login.email),
|
|
1487
|
+
)
|
|
1488
|
+
return AlexaAPI.devices[login.email]
|
|
1489
|
+
|
|
1490
|
+
_LOGGER.warning(
|
|
1491
|
+
"%s: Device API returned no data and no cached device list is available",
|
|
1492
|
+
hide_email(login.email),
|
|
1480
1493
|
)
|
|
1494
|
+
AlexaAPI.devices[login.email] = []
|
|
1481
1495
|
return AlexaAPI.devices[login.email]
|
|
1482
1496
|
|
|
1483
1497
|
@staticmethod
|
|
@@ -63,6 +63,17 @@ from .helpers import (
|
|
|
63
63
|
_LOGGER = logging.getLogger(__name__)
|
|
64
64
|
|
|
65
65
|
|
|
66
|
+
# Ensure http.cookies.Morsel recognizes the Partitioned cookie attribute.
|
|
67
|
+
# Older Python releases do not include the "partitioned" attribute.
|
|
68
|
+
# Newer releases (Python 3.14+) already support it, so this patch is only
|
|
69
|
+
# applied when necessary.
|
|
70
|
+
if "partitioned" not in Morsel._reserved:
|
|
71
|
+
_LOGGER.debug("Adding partitioned support to http.cookies.Morsel")
|
|
72
|
+
Morsel._reserved["partitioned"] = "Partitioned"
|
|
73
|
+
|
|
74
|
+
Morsel._flags.add("partitioned")
|
|
75
|
+
|
|
76
|
+
|
|
66
77
|
def create_alexa_context() -> ssl.SSLContext:
|
|
67
78
|
"""Create an SSL context for Alexa."""
|
|
68
79
|
context = ssl.create_default_context(
|
|
@@ -203,9 +214,12 @@ def _serialize_cookie_jar(cookie_jar: aiohttp.CookieJar) -> dict[str, Any]:
|
|
|
203
214
|
|
|
204
215
|
|
|
205
216
|
def _deserialize_cookie(
|
|
206
|
-
cookie_jar: aiohttp.CookieJar,
|
|
217
|
+
cookie_jar: aiohttp.CookieJar,
|
|
218
|
+
cookie: dict[str, Any],
|
|
219
|
+
*,
|
|
220
|
+
fallback_domain: str = "",
|
|
207
221
|
) -> tuple[str, str] | None:
|
|
208
|
-
"""Restore
|
|
222
|
+
"""Restore a serialized cookie into an aiohttp CookieJar."""
|
|
209
223
|
name = str(cookie.get("name") or "")
|
|
210
224
|
value = cookie.get("value")
|
|
211
225
|
domain = str(cookie.get("domain") or "")
|
|
@@ -221,12 +235,15 @@ def _deserialize_cookie(
|
|
|
221
235
|
if not name or value is None:
|
|
222
236
|
return None
|
|
223
237
|
|
|
238
|
+
if partitioned:
|
|
239
|
+
secure = True
|
|
240
|
+
|
|
224
241
|
cookie_value = _strip_wrapping_quotes(str(value))
|
|
225
242
|
raw_cookie = SimpleCookie()
|
|
226
243
|
raw_cookie[name] = cookie_value
|
|
227
244
|
morsel = raw_cookie[name]
|
|
228
245
|
|
|
229
|
-
clean_domain = domain.lstrip(".")
|
|
246
|
+
clean_domain = domain.lstrip(".") or fallback_domain.lstrip(".")
|
|
230
247
|
clean_path = path
|
|
231
248
|
if not clean_path.startswith("/"):
|
|
232
249
|
clean_path = f"/{clean_path}"
|
|
@@ -255,7 +272,6 @@ def _deserialize_cookie(
|
|
|
255
272
|
with contextlib.suppress(KeyError):
|
|
256
273
|
morsel["samesite"] = samesite
|
|
257
274
|
if partitioned:
|
|
258
|
-
# Only Python/aiohttp combinations that know the attr should keep it.
|
|
259
275
|
with contextlib.suppress(KeyError):
|
|
260
276
|
morsel["partitioned"] = True
|
|
261
277
|
|
|
@@ -264,7 +280,10 @@ def _deserialize_cookie(
|
|
|
264
280
|
|
|
265
281
|
|
|
266
282
|
def _deserialize_cookie_jar(
|
|
267
|
-
cookie_jar: aiohttp.CookieJar,
|
|
283
|
+
cookie_jar: aiohttp.CookieJar,
|
|
284
|
+
serialized: dict[str, Any],
|
|
285
|
+
*,
|
|
286
|
+
fallback_domain: str = "",
|
|
268
287
|
) -> dict[str, str]:
|
|
269
288
|
"""Restore a serialized alexapy cookie jar into an aiohttp CookieJar."""
|
|
270
289
|
return_cookies: dict[str, str] = {}
|
|
@@ -277,7 +296,11 @@ def _deserialize_cookie_jar(
|
|
|
277
296
|
for cookie in serialized.get("cookies", []):
|
|
278
297
|
if not isinstance(cookie, dict):
|
|
279
298
|
continue
|
|
280
|
-
restored = _deserialize_cookie(
|
|
299
|
+
restored = _deserialize_cookie(
|
|
300
|
+
cookie_jar,
|
|
301
|
+
cookie,
|
|
302
|
+
fallback_domain=fallback_domain,
|
|
303
|
+
)
|
|
281
304
|
if restored:
|
|
282
305
|
return_cookies[restored[0]] = restored[1]
|
|
283
306
|
|
|
@@ -294,7 +317,10 @@ def _legacy_cookie_value(value: Any) -> Any:
|
|
|
294
317
|
|
|
295
318
|
|
|
296
319
|
def _restore_legacy_aiohttp_cookie_mapping(
|
|
297
|
-
cookie_jar: aiohttp.CookieJar,
|
|
320
|
+
cookie_jar: aiohttp.CookieJar,
|
|
321
|
+
cookies: dict[Any, Any],
|
|
322
|
+
*,
|
|
323
|
+
fallback_domain: str = "",
|
|
298
324
|
) -> dict[str, str]:
|
|
299
325
|
"""Restore old aiohttp.CookieJar.save() mappings.
|
|
300
326
|
|
|
@@ -304,7 +330,7 @@ def _restore_legacy_aiohttp_cookie_mapping(
|
|
|
304
330
|
restored_count = 0
|
|
305
331
|
|
|
306
332
|
for bucket_key, bucket in cookies.items():
|
|
307
|
-
domain =
|
|
333
|
+
domain = fallback_domain
|
|
308
334
|
path = "/"
|
|
309
335
|
if isinstance(bucket_key, tuple):
|
|
310
336
|
domain = str(bucket_key[0] or "") if len(bucket_key) >= 1 else ""
|
|
@@ -362,6 +388,7 @@ def _restore_legacy_aiohttp_cookie_mapping(
|
|
|
362
388
|
"samesite": samesite,
|
|
363
389
|
"partitioned": partitioned,
|
|
364
390
|
},
|
|
391
|
+
fallback_domain=fallback_domain,
|
|
365
392
|
)
|
|
366
393
|
if restored:
|
|
367
394
|
restored_count += 1
|
|
@@ -718,7 +745,11 @@ class AlexaLogin:
|
|
|
718
745
|
):
|
|
719
746
|
_LOGGER.debug("Loading serialized alexapy cookie jar")
|
|
720
747
|
cookie_jar = self._session.cookie_jar
|
|
721
|
-
return_cookies = _deserialize_cookie_jar(
|
|
748
|
+
return_cookies = _deserialize_cookie_jar(
|
|
749
|
+
cookie_jar,
|
|
750
|
+
cookies,
|
|
751
|
+
fallback_domain=self.url,
|
|
752
|
+
)
|
|
722
753
|
numcookies = len(return_cookies)
|
|
723
754
|
elif isinstance(cookies, RequestsCookieJar):
|
|
724
755
|
_LOGGER.debug("Loading RequestsCookieJar")
|
|
@@ -726,15 +757,15 @@ class AlexaLogin:
|
|
|
726
757
|
for key, value in requests_cookies.items():
|
|
727
758
|
if self._debug:
|
|
728
759
|
_LOGGER.debug('Key: "%s", Value: "%s"', key, value)
|
|
729
|
-
|
|
730
|
-
if key != "partitioned":
|
|
731
|
-
return_cookies[str(key)] = value.strip('"')
|
|
760
|
+
return_cookies[str(key)] = value.strip('"')
|
|
732
761
|
numcookies = len(return_cookies)
|
|
733
762
|
elif isinstance(cookies, defaultdict):
|
|
734
763
|
_LOGGER.debug("Loading legacy aiohttp cookie mapping")
|
|
735
764
|
cookie_jar = self._session.cookie_jar
|
|
736
765
|
return_cookies = _restore_legacy_aiohttp_cookie_mapping(
|
|
737
|
-
cookie_jar,
|
|
766
|
+
cookie_jar,
|
|
767
|
+
cookies,
|
|
768
|
+
fallback_domain=self.url,
|
|
738
769
|
)
|
|
739
770
|
numcookies = len(return_cookies)
|
|
740
771
|
elif isinstance(cookies, dict):
|
|
@@ -743,14 +774,15 @@ class AlexaLogin:
|
|
|
743
774
|
return_cookies = {
|
|
744
775
|
str(key): str(value).strip('"')
|
|
745
776
|
for key, value in cookies.items()
|
|
746
|
-
if key != "partitioned"
|
|
747
777
|
}
|
|
748
778
|
numcookies = len(return_cookies)
|
|
749
779
|
else:
|
|
750
780
|
_LOGGER.debug("Loading legacy aiohttp cookie dict mapping")
|
|
751
781
|
cookie_jar = self._session.cookie_jar
|
|
752
782
|
return_cookies = _restore_legacy_aiohttp_cookie_mapping(
|
|
753
|
-
cookie_jar,
|
|
783
|
+
cookie_jar,
|
|
784
|
+
cookies,
|
|
785
|
+
fallback_domain=self.url,
|
|
754
786
|
)
|
|
755
787
|
numcookies = len(return_cookies)
|
|
756
788
|
elif isinstance(cookies, http.cookiejar.MozillaCookieJar):
|
|
@@ -1219,11 +1251,6 @@ class AlexaLogin:
|
|
|
1219
1251
|
self._cookiefile[0].replace(self.email, hide_email(self.email)),
|
|
1220
1252
|
EXCEPTION_TEMPLATE.format(type(ex).__name__, ex.args),
|
|
1221
1253
|
)
|
|
1222
|
-
if self._debug:
|
|
1223
|
-
_LOGGER.debug(
|
|
1224
|
-
"Deleted:\n%s",
|
|
1225
|
-
self._cookiefile.replace(self.email, hide_email(self.email))
|
|
1226
|
-
)
|
|
1227
1254
|
|
|
1228
1255
|
async def get_tokens(self) -> bool:
|
|
1229
1256
|
"""Get access and refresh tokens after registering device using cookies.
|
|
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
|