aioamazondevices 0.1.0__tar.gz → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: aioamazondevices
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Python library to control Amazon devices
5
5
  Home-page: https://github.com/chemelli74/aioamazondevices
6
6
  License: Apache Software License 2.0
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "aioamazondevices"
3
- version = "0.1.0"
3
+ version = "0.2.0"
4
4
  description = "Python library to control Amazon devices"
5
5
  authors = ["Simone Chemelli <simone.chemelli@gmail.com>"]
6
6
  license = "Apache Software License 2.0"
@@ -1,6 +1,6 @@
1
1
  """aioamazondevices library."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.2.0"
4
4
 
5
5
 
6
6
  from .api import AmazonDevice, AmazonEchoApi
@@ -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,25 +48,50 @@ 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
- locale = DOMAIN_BY_COUNTRY.get(login_country_code)
74
- domain = locale["domain"] if locale else login_country_code
54
+ # Force country digits as lower case
55
+ country_code = login_country_code.lower()
56
+
57
+ locale = DOMAIN_BY_COUNTRY.get(country_code)
58
+ domain = locale["domain"] if locale else country_code
75
59
 
76
60
  assoc_handle = "amzn_dp_project_dee_ios"
77
61
  if not locale:
78
- assoc_handle += f"_{login_country_code}"
62
+ assoc_handle += f"_{country_code}"
79
63
  self._assoc_handle = assoc_handle
80
64
 
81
65
  self._login_email = login_email
82
66
  self._login_password = login_password
83
67
  self._domain = domain
84
68
  self._url = f"https://www.amazon.{domain}"
85
- self._cookies = _build_init_cookies()
69
+ self._cookies = self._build_init_cookies()
86
70
  self._headers = DEFAULT_HEADERS
71
+ self._save_html = save_html
87
72
 
88
73
  self.session: AsyncClient
89
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
+
90
95
  def _create_code_verifier(self, length: int = 32) -> bytes:
91
96
  """Create code verifier."""
92
97
  verifier = secrets.token_bytes(length)
@@ -97,18 +102,18 @@ class AmazonEchoApi:
97
102
  m = hashlib.sha256(verifier)
98
103
  return base64.urlsafe_b64encode(m.digest()).rstrip(b"=")
99
104
 
100
- def _build_client_id(self) -> str:
105
+ def _build_client_id(self, serial: str) -> str:
101
106
  """Build client ID."""
102
- serial = uuid.uuid4().hex.upper()
103
107
  client_id = serial.encode() + AMAZON_SERIAL_NUMBER
104
108
  return client_id.hex()
105
109
 
106
110
  def _build_oauth_url(
107
111
  self,
112
+ code_verifier: bytes,
113
+ client_id: str,
108
114
  ) -> str:
109
115
  """Build the url to login to Amazon as a Mobile device."""
110
- client_id = self._build_client_id()
111
- code_challenge = self._create_s256_code_challenge(self._create_code_verifier())
116
+ code_challenge = self._create_s256_code_challenge(code_verifier)
112
117
 
113
118
  oauth_params = {
114
119
  "openid.oa2.response_type": "code",
@@ -180,15 +185,34 @@ class AmazonEchoApi:
180
185
  url,
181
186
  data=input_data,
182
187
  )
188
+
189
+ await self._save_to_file(
190
+ resp.text,
191
+ url,
192
+ )
183
193
  return BeautifulSoup(resp.content, "html.parser"), resp
184
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
+
185
206
  async def login(self, otp_code: str) -> bool:
186
207
  """Login to Amazon."""
187
208
  _LOGGER.debug("Logging-in for %s [otp code %s]", self._login_email, otp_code)
188
209
  self._client_session()
189
210
 
211
+ code_verifier = self._create_code_verifier()
212
+ client_id = self._build_client_id(uuid.uuid4().hex.upper())
213
+
190
214
  _LOGGER.debug("Build oauth URL")
191
- login_url = self._build_oauth_url()
215
+ login_url = self._build_oauth_url(code_verifier, client_id)
192
216
 
193
217
  login_soup, _ = await self._session_request("GET", login_url)
194
218
  login_method, login_url = self._get_request_from_soup(login_soup)
@@ -196,6 +220,7 @@ class AmazonEchoApi:
196
220
  login_inputs["email"] = self._login_email
197
221
  login_inputs["password"] = self._login_password
198
222
 
223
+ _LOGGER.debug("Register at %s", login_url)
199
224
  login_soup, _ = await self._session_request(
200
225
  login_method,
201
226
  login_url,