pynintendoparental 0.7.0__tar.gz → 0.7.2__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.
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/PKG-INFO +1 -1
- pynintendoparental-0.7.2/pynintendoparental/_version.py +1 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/api.py +6 -23
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/authenticator/__init__.py +42 -25
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/device.py +8 -1
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/PKG-INFO +1 -1
- pynintendoparental-0.7.0/pynintendoparental/_version.py +0 -1
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/LICENSE +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/README.md +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/__init__.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/application.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/authenticator/const.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/const.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/enum.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/exceptions.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/player.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/py.typed +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/utils.py +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/SOURCES.txt +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/dependency_links.txt +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/requires.txt +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/top_level.txt +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pyproject.toml +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/setup.cfg +0 -0
- {pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.2"
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
"""API handler."""
|
|
2
2
|
|
|
3
|
-
from datetime import datetime, timedelta
|
|
4
|
-
|
|
5
3
|
import aiohttp
|
|
6
4
|
|
|
7
5
|
from .authenticator import Authenticator
|
|
@@ -25,20 +23,11 @@ def _check_http_success(status: int) -> bool:
|
|
|
25
23
|
class Api:
|
|
26
24
|
"""Nintendo Parental Controls API."""
|
|
27
25
|
|
|
28
|
-
def __init__(self, auth, tz, lang
|
|
26
|
+
def __init__(self, auth, tz, lang):
|
|
29
27
|
"""INIT"""
|
|
30
28
|
self._auth: Authenticator = auth
|
|
31
29
|
self._tz = tz
|
|
32
30
|
self._language = lang
|
|
33
|
-
if session is None:
|
|
34
|
-
session = aiohttp.ClientSession()
|
|
35
|
-
self._session_created_internally = True
|
|
36
|
-
self._session = session
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def _auth_token(self) -> str:
|
|
40
|
-
"""Returns the auth token."""
|
|
41
|
-
return f"Bearer {self._auth.access_token}"
|
|
42
31
|
|
|
43
32
|
@property
|
|
44
33
|
def account_id(self):
|
|
@@ -59,15 +48,9 @@ class Api:
|
|
|
59
48
|
"X-Moon-TimeZone": self._tz,
|
|
60
49
|
"X-Moon-Os-Language": self._language,
|
|
61
50
|
"X-Moon-App-Language": self._language,
|
|
62
|
-
"Authorization": self.
|
|
51
|
+
"Authorization": self._auth.access_token
|
|
63
52
|
}
|
|
64
53
|
|
|
65
|
-
async def async_close(self):
|
|
66
|
-
"""Closes the underlying aiohttp.ClientSession if it was created by this instance."""
|
|
67
|
-
if hasattr(self, '_session_created_internally') and self._session_created_internally and self._session and not self._session.closed:
|
|
68
|
-
await self._session.close()
|
|
69
|
-
self._session = None # Optional: clear the session attribute
|
|
70
|
-
|
|
71
54
|
async def send_request(self, endpoint: str, body: object=None, **kwargs):
|
|
72
55
|
"""Sends a request to a given endpoint."""
|
|
73
56
|
_LOGGER.debug("Sending request to %s", endpoint)
|
|
@@ -76,7 +59,7 @@ class Api:
|
|
|
76
59
|
if e_point is None:
|
|
77
60
|
raise ValueError("Endpoint does not exist")
|
|
78
61
|
# refresh the token if it has expired.
|
|
79
|
-
if self._auth.
|
|
62
|
+
if self._auth.access_token_expired:
|
|
80
63
|
_LOGGER.debug("Access token expired, requesting refresh.")
|
|
81
64
|
await self._auth.perform_refresh()
|
|
82
65
|
# format the URL using the kwargs
|
|
@@ -89,11 +72,11 @@ class Api:
|
|
|
89
72
|
"json": "",
|
|
90
73
|
"headers": ""
|
|
91
74
|
}
|
|
92
|
-
self.
|
|
93
|
-
async with self._session.request(
|
|
75
|
+
async with self._auth.client_session.request(
|
|
94
76
|
method=e_point.get("method"),
|
|
95
77
|
url=url,
|
|
96
|
-
json=body
|
|
78
|
+
json=body,
|
|
79
|
+
headers=self._headers
|
|
97
80
|
) as response:
|
|
98
81
|
_LOGGER.debug("%s request to %s status code %s",
|
|
99
82
|
e_point.get("method"),
|
{pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/authenticator/__init__.py
RENAMED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
|
-
import re
|
|
6
5
|
import base64
|
|
7
6
|
import hashlib
|
|
8
7
|
import random
|
|
@@ -60,11 +59,16 @@ def _rand():
|
|
|
60
59
|
class Authenticator:
|
|
61
60
|
"""Authentication functions."""
|
|
62
61
|
|
|
63
|
-
def __init__(
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
session_token = None,
|
|
65
|
+
auth_code_verifier = None,
|
|
66
|
+
client_session: aiohttp.ClientSession = None
|
|
67
|
+
):
|
|
64
68
|
"""Basic init."""
|
|
65
69
|
_LOGGER.debug(">> Init authenticator.")
|
|
66
|
-
self.
|
|
67
|
-
self.
|
|
70
|
+
self._at_expiry: datetime = None
|
|
71
|
+
self._access_token: str = None
|
|
68
72
|
self.available_scopes: dict = None
|
|
69
73
|
self.account_id: str = None
|
|
70
74
|
self.account: dict = None
|
|
@@ -73,12 +77,25 @@ class Authenticator:
|
|
|
73
77
|
self._id_token: str = None
|
|
74
78
|
self._session_token: str = session_token
|
|
75
79
|
self.login_url: str = None
|
|
80
|
+
if client_session is None:
|
|
81
|
+
client_session = aiohttp.ClientSession()
|
|
82
|
+
self.client_session: aiohttp.ClientSession = client_session
|
|
76
83
|
|
|
77
84
|
@property
|
|
78
85
|
def get_session_token(self) -> str:
|
|
79
86
|
"""Return the session token."""
|
|
80
87
|
return self._session_token
|
|
81
88
|
|
|
89
|
+
@property
|
|
90
|
+
def access_token(self) -> str:
|
|
91
|
+
"""Return the formatted access token."""
|
|
92
|
+
return f"Bearer {self._access_token}"
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def access_token_expired(self) -> bool:
|
|
96
|
+
"""Check if the access token has expired."""
|
|
97
|
+
return self._at_expiry < (datetime.now()+timedelta(minutes=1))
|
|
98
|
+
|
|
82
99
|
async def _request_handler(self, method, url, json=None, data=None, headers: dict=None):
|
|
83
100
|
"""Send a HTTP request"""
|
|
84
101
|
if headers is None:
|
|
@@ -89,28 +106,25 @@ class Authenticator:
|
|
|
89
106
|
"json": "",
|
|
90
107
|
"headers": ""
|
|
91
108
|
}
|
|
92
|
-
async with
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
response["text"] = await resp.text()
|
|
104
|
-
response["json"] = await resp.json()
|
|
105
|
-
response["headers"] = resp.headers
|
|
109
|
+
async with self.client_session.request(
|
|
110
|
+
method=method,
|
|
111
|
+
url=url,
|
|
112
|
+
json=json,
|
|
113
|
+
data=data,
|
|
114
|
+
headers=headers
|
|
115
|
+
) as resp:
|
|
116
|
+
response["status"] = resp.status
|
|
117
|
+
response["text"] = await resp.text()
|
|
118
|
+
response["json"] = await resp.json()
|
|
119
|
+
response["headers"] = resp.headers
|
|
106
120
|
return response
|
|
107
121
|
|
|
108
122
|
def _read_tokens(self, tokens: dict):
|
|
109
123
|
"""Reads tokens into self."""
|
|
110
124
|
self.available_scopes = tokens.get("scope")
|
|
111
|
-
self.
|
|
125
|
+
self._at_expiry = datetime.now() + timedelta(seconds=tokens.get("expires_in"))
|
|
112
126
|
self._id_token = tokens.get("id_token")
|
|
113
|
-
self.
|
|
127
|
+
self._access_token = tokens.get("access_token")
|
|
114
128
|
|
|
115
129
|
async def perform_login(self, session_token_code):
|
|
116
130
|
"""Retrieves initial tokens."""
|
|
@@ -159,7 +173,7 @@ class Authenticator:
|
|
|
159
173
|
method="GET",
|
|
160
174
|
url=MY_ACCOUNT_ENDPOINT,
|
|
161
175
|
headers={
|
|
162
|
-
"Authorization":
|
|
176
|
+
"Authorization": self.access_token
|
|
163
177
|
}
|
|
164
178
|
)
|
|
165
179
|
if account["status"] != 200:
|
|
@@ -168,11 +182,13 @@ class Authenticator:
|
|
|
168
182
|
self.account = account["json"]
|
|
169
183
|
|
|
170
184
|
@classmethod
|
|
171
|
-
def generate_login(
|
|
185
|
+
def generate_login(
|
|
186
|
+
cls,
|
|
187
|
+
client_session: aiohttp.ClientSession | None = None) -> 'Authenticator':
|
|
172
188
|
"""Starts configuration of the authenticator."""
|
|
173
189
|
verifier = _rand()
|
|
174
190
|
|
|
175
|
-
auth = cls(auth_code_verifier=verifier)
|
|
191
|
+
auth = cls(auth_code_verifier=verifier, client_session=client_session)
|
|
176
192
|
|
|
177
193
|
query = {
|
|
178
194
|
"client_id": CLIENT_ID,
|
|
@@ -193,10 +209,11 @@ class Authenticator:
|
|
|
193
209
|
async def complete_login(cls,
|
|
194
210
|
auth: Authenticator | None,
|
|
195
211
|
response_token: str,
|
|
196
|
-
is_session_token: bool=False
|
|
212
|
+
is_session_token: bool=False,
|
|
213
|
+
client_session: aiohttp.ClientSession | None = None) -> Authenticator:
|
|
197
214
|
"""Creates and logs into Nintendo APIs"""
|
|
198
215
|
if is_session_token:
|
|
199
|
-
auth = cls(session_token=response_token)
|
|
216
|
+
auth = cls(session_token=response_token, client_session=client_session)
|
|
200
217
|
await auth.perform_refresh()
|
|
201
218
|
else:
|
|
202
219
|
response_token = _parse_response_token(response_token)
|
|
@@ -76,6 +76,13 @@ class Device:
|
|
|
76
76
|
if callback not in self._callbacks:
|
|
77
77
|
self._callbacks.append(callback)
|
|
78
78
|
|
|
79
|
+
def remove_device_callback(self, callback):
|
|
80
|
+
"""Remove a given device callback."""
|
|
81
|
+
if not callable(callback):
|
|
82
|
+
raise ValueError("Object must be callable.")
|
|
83
|
+
if callback in self._callbacks:
|
|
84
|
+
self._callbacks.remove(callback)
|
|
85
|
+
|
|
79
86
|
async def set_new_pin(self, pin: str):
|
|
80
87
|
"""Updates the pin for the device."""
|
|
81
88
|
_LOGGER.debug(">> Device.set_new_pin(pin=REDACTED)")
|
|
@@ -273,7 +280,7 @@ class Device:
|
|
|
273
280
|
time_remaining_by_bedtime = 0.0
|
|
274
281
|
if bedtime_dt > now: # Bedtime is in the future today
|
|
275
282
|
time_remaining_by_bedtime = (bedtime_dt - now).total_seconds() / 60
|
|
276
|
-
time_remaining_by_bedtime = max(0.0, time_remaining_by_bedtime)
|
|
283
|
+
time_remaining_by_bedtime = max(0.0, time_remaining_by_bedtime)
|
|
277
284
|
# else: Bedtime has passed for today or is now, so time_remaining_by_bedtime remains 0.0
|
|
278
285
|
|
|
279
286
|
effective_remaining_time = min(effective_remaining_time, time_remaining_by_bedtime)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.7.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental/authenticator/const.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/requires.txt
RENAMED
|
File without changes
|
{pynintendoparental-0.7.0 → pynintendoparental-0.7.2}/pynintendoparental.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|