pynintendoparental 1.1.2__py3-none-any.whl → 2.0.0__py3-none-any.whl
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/__init__.py +11 -13
- pynintendoparental/_version.py +1 -1
- pynintendoparental/api.py +68 -84
- pynintendoparental/application.py +17 -11
- pynintendoparental/authenticator.py +18 -0
- pynintendoparental/const.py +21 -12
- pynintendoparental/device.py +300 -204
- pynintendoparental/enum.py +15 -1
- pynintendoparental/exceptions.py +4 -17
- pynintendoparental/player.py +3 -1
- pynintendoparental/utils.py +1 -0
- {pynintendoparental-1.1.2.dist-info → pynintendoparental-2.0.0.dist-info}/METADATA +3 -1
- pynintendoparental-2.0.0.dist-info/RECORD +17 -0
- pynintendoparental/authenticator/__init__.py +0 -226
- pynintendoparental/authenticator/const.py +0 -29
- pynintendoparental-1.1.2.dist-info/RECORD +0 -18
- {pynintendoparental-1.1.2.dist-info → pynintendoparental-2.0.0.dist-info}/WHEEL +0 -0
- {pynintendoparental-1.1.2.dist-info → pynintendoparental-2.0.0.dist-info}/licenses/LICENSE +0 -0
- {pynintendoparental-1.1.2.dist-info → pynintendoparental-2.0.0.dist-info}/top_level.txt +0 -0
pynintendoparental/enum.py
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"""Enums"""
|
|
2
2
|
|
|
3
|
-
from enum import Enum
|
|
3
|
+
from enum import Enum, StrEnum
|
|
4
|
+
|
|
4
5
|
|
|
5
6
|
class AlarmSettingState(Enum):
|
|
6
7
|
"""Alarm setting states."""
|
|
8
|
+
|
|
7
9
|
SUCCESS = 0
|
|
8
10
|
TO_VISIBLE = 1
|
|
9
11
|
TO_INVISIBLE = 2
|
|
@@ -13,10 +15,22 @@ class AlarmSettingState(Enum):
|
|
|
13
15
|
def __str__(self) -> str:
|
|
14
16
|
return self.name
|
|
15
17
|
|
|
18
|
+
|
|
16
19
|
class RestrictionMode(Enum):
|
|
17
20
|
"""Restriction modes."""
|
|
21
|
+
|
|
18
22
|
FORCED_TERMINATION = 0
|
|
19
23
|
ALARM = 1
|
|
20
24
|
|
|
21
25
|
def __str__(self) -> str:
|
|
22
26
|
return self.name
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DeviceTimerMode(StrEnum):
|
|
30
|
+
"""Device timer modes."""
|
|
31
|
+
|
|
32
|
+
DAILY = "DAILY"
|
|
33
|
+
EACH_DAY_OF_THE_WEEK = "EACH_DAY_OF_THE_WEEK"
|
|
34
|
+
|
|
35
|
+
def __str__(self) -> str:
|
|
36
|
+
return self.name
|
pynintendoparental/exceptions.py
CHANGED
|
@@ -8,28 +8,13 @@ class RangeErrorKeys(StrEnum):
|
|
|
8
8
|
DAILY_PLAYTIME = "daily_playtime_out_of_range"
|
|
9
9
|
BEDTIME = "bedtime_alarm_out_of_range"
|
|
10
10
|
|
|
11
|
-
class HttpException(Exception):
|
|
12
|
-
"""A HTTP error occured"""
|
|
13
|
-
def __init__(self, status_code: int, message: str) -> None:
|
|
14
|
-
"""Initialize the exception."""
|
|
15
|
-
super().__init__(message)
|
|
16
|
-
self.status_code = status_code
|
|
17
|
-
self.message = message
|
|
18
|
-
|
|
19
|
-
def __str__(self) -> str:
|
|
20
|
-
return f"HTTP {self.status_code}: {self.message}"
|
|
21
|
-
|
|
22
|
-
class InvalidSessionTokenException(HttpException):
|
|
23
|
-
"""Provided session token was invalid (invalid_grant)."""
|
|
24
|
-
|
|
25
|
-
class InvalidOAuthConfigurationException(HttpException):
|
|
26
|
-
"""The OAuth scopes are invalid."""
|
|
27
|
-
|
|
28
11
|
class NoDevicesFoundException(Exception):
|
|
29
12
|
"""No devices were found for the account."""
|
|
30
13
|
|
|
14
|
+
|
|
31
15
|
class InputValidationError(Exception):
|
|
32
16
|
"""Input Validation Failed."""
|
|
17
|
+
|
|
33
18
|
value: object
|
|
34
19
|
error_key: str
|
|
35
20
|
|
|
@@ -37,11 +22,13 @@ class InputValidationError(Exception):
|
|
|
37
22
|
super().__init__(f"{self.__doc__} Received value: {value}")
|
|
38
23
|
self.value = value
|
|
39
24
|
|
|
25
|
+
|
|
40
26
|
class BedtimeOutOfRangeError(InputValidationError):
|
|
41
27
|
"""Bedtime is outside of the allowed range."""
|
|
42
28
|
|
|
43
29
|
error_key = RangeErrorKeys.BEDTIME
|
|
44
30
|
|
|
31
|
+
|
|
45
32
|
class DailyPlaytimeOutOfRangeError(InputValidationError):
|
|
46
33
|
"""Daily playtime is outside of the allowed range."""
|
|
47
34
|
|
pynintendoparental/player.py
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from .const import _LOGGER
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
class Player:
|
|
6
7
|
"""Defines a single player on a Nintendo device."""
|
|
8
|
+
|
|
7
9
|
def __init__(self):
|
|
8
10
|
"""Init a player."""
|
|
9
11
|
self.player_image: str = None
|
|
@@ -25,7 +27,7 @@ class Player:
|
|
|
25
27
|
break
|
|
26
28
|
|
|
27
29
|
@classmethod
|
|
28
|
-
def from_device_daily_summary(cls, raw: list[dict]) -> list[
|
|
30
|
+
def from_device_daily_summary(cls, raw: list[dict]) -> list["Player"]:
|
|
29
31
|
"""Converts a daily summary response into a list of players."""
|
|
30
32
|
players = []
|
|
31
33
|
_LOGGER.debug("Building players from device daily summary.")
|
pynintendoparental/utils.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pynintendoparental
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: A Python module to interact with Nintendo Parental Controls
|
|
5
5
|
Home-page: http://github.com/pantherale0/pynintendoparental
|
|
6
6
|
Author: pantherale0
|
|
@@ -11,6 +11,7 @@ Classifier: Operating System :: OS Independent
|
|
|
11
11
|
Requires-Python: >=3.8, <4
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
|
+
Requires-Dist: pynintendoauth~=1.0.0
|
|
14
15
|
Provides-Extra: dev
|
|
15
16
|
Requires-Dist: bandit<1.9,>=1.7; extra == "dev"
|
|
16
17
|
Requires-Dist: black<26,>=23; extra == "dev"
|
|
@@ -29,6 +30,7 @@ Dynamic: home-page
|
|
|
29
30
|
Dynamic: license
|
|
30
31
|
Dynamic: license-file
|
|
31
32
|
Dynamic: provides-extra
|
|
33
|
+
Dynamic: requires-dist
|
|
32
34
|
Dynamic: requires-python
|
|
33
35
|
Dynamic: summary
|
|
34
36
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
pynintendoparental/__init__.py,sha256=gO3rH9gukrFoACVkdqj_liqcgtHxSZlitE0PrVQZGok,2334
|
|
2
|
+
pynintendoparental/_version.py,sha256=_7OlQdbVkK4jad0CLdpI0grT-zEAb-qgFmH5mFzDXiA,22
|
|
3
|
+
pynintendoparental/api.py,sha256=LmZei8WPzEhCEoVLXXHiaKRbDEbmGyD6CyfPS4fXxnc,7047
|
|
4
|
+
pynintendoparental/application.py,sha256=8zTisF3_COgIzKCLIgLpYdHcX4OiwRQU5NH2WUngvxc,3995
|
|
5
|
+
pynintendoparental/authenticator.py,sha256=WPAEAUKmIymIiQUXILYt4B2_3UgYRKEVi6btJRwzjmM,430
|
|
6
|
+
pynintendoparental/const.py,sha256=2fq0VmqFetwLq1YBXatlb7tFaDeR0pnsdja4MLFdbDA,2174
|
|
7
|
+
pynintendoparental/device.py,sha256=VSK1-fKcXA2AoGCKaVGyhy32T6VXYowuo7upNivaWyk,24521
|
|
8
|
+
pynintendoparental/enum.py,sha256=NlkfrZTfiur6vTqqwHxeiM7wPQZ-1Y1PlY4Jh_gq4NQ,605
|
|
9
|
+
pynintendoparental/exceptions.py,sha256=8a_7ocrlB0PwPM36Q4FqR-W0lkGXihOs79gGqQIWZoo,881
|
|
10
|
+
pynintendoparental/player.py,sha256=rXYgUiy7DhBjosQlADr7Fa_HVdA1e-04oPZiC4Y68oM,1787
|
|
11
|
+
pynintendoparental/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
|
+
pynintendoparental/utils.py,sha256=gLMibsEOnKUZJgCQKF4Zk517fawZ3mBqMK6MS2g-Um0,199
|
|
13
|
+
pynintendoparental-2.0.0.dist-info/licenses/LICENSE,sha256=zsxHgHVMnyWq121yND8zBl9Rl9H6EF2K9N51B2ZSm_k,1071
|
|
14
|
+
pynintendoparental-2.0.0.dist-info/METADATA,sha256=5LTsNbTAUk98bovyA4h57PuFT6-iC470Hk98ErArp7Q,1931
|
|
15
|
+
pynintendoparental-2.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
pynintendoparental-2.0.0.dist-info/top_level.txt,sha256=QQ5bAl-Ljso16P8KLf1NHrFmKk9jLT7bVJG_rVlIXWk,19
|
|
17
|
+
pynintendoparental-2.0.0.dist-info/RECORD,,
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
"""Nintendo Authentication."""
|
|
2
|
-
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
import logging
|
|
5
|
-
import base64
|
|
6
|
-
import hashlib
|
|
7
|
-
import random
|
|
8
|
-
import string
|
|
9
|
-
|
|
10
|
-
from urllib.parse import urlencode, urlparse
|
|
11
|
-
|
|
12
|
-
from datetime import datetime, timedelta
|
|
13
|
-
|
|
14
|
-
import aiohttp
|
|
15
|
-
|
|
16
|
-
from pynintendoparental.exceptions import (
|
|
17
|
-
HttpException,
|
|
18
|
-
InvalidOAuthConfigurationException,
|
|
19
|
-
InvalidSessionTokenException
|
|
20
|
-
)
|
|
21
|
-
from .const import (
|
|
22
|
-
TOKEN_URL,
|
|
23
|
-
SESSION_TOKEN_URL,
|
|
24
|
-
CLIENT_ID,
|
|
25
|
-
GRANT_TYPE,
|
|
26
|
-
MY_ACCOUNT_ENDPOINT,
|
|
27
|
-
REDIRECT_URI,
|
|
28
|
-
SCOPES,
|
|
29
|
-
AUTHORIZE_URL
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
_LOGGER = logging.getLogger(__name__)
|
|
33
|
-
|
|
34
|
-
def _parse_response_token(token: str) -> dict:
|
|
35
|
-
"""Parses a response token."""
|
|
36
|
-
_LOGGER.debug(">> Parsing response token.")
|
|
37
|
-
try:
|
|
38
|
-
url = urlparse(token)
|
|
39
|
-
params = url.fragment.split('&')
|
|
40
|
-
response = {}
|
|
41
|
-
for param in params:
|
|
42
|
-
response = {
|
|
43
|
-
**response,
|
|
44
|
-
param.split('=')[0]: param.split('=')[1]
|
|
45
|
-
}
|
|
46
|
-
return response
|
|
47
|
-
except Exception as exc:
|
|
48
|
-
raise ValueError("Invalid token provided.") from exc
|
|
49
|
-
|
|
50
|
-
def _hash(text: str):
|
|
51
|
-
"""Hash given text for login."""
|
|
52
|
-
text = hashlib.sha256(text.encode()).digest()
|
|
53
|
-
text = base64.urlsafe_b64encode(text).decode()
|
|
54
|
-
return text.replace("=", "")
|
|
55
|
-
|
|
56
|
-
def _rand():
|
|
57
|
-
return ''.join(random.choice(string.ascii_letters) for _ in range(50))
|
|
58
|
-
|
|
59
|
-
class Authenticator:
|
|
60
|
-
"""Authentication functions."""
|
|
61
|
-
|
|
62
|
-
def __init__(
|
|
63
|
-
self,
|
|
64
|
-
session_token = None,
|
|
65
|
-
auth_code_verifier = None,
|
|
66
|
-
client_session: aiohttp.ClientSession = None
|
|
67
|
-
):
|
|
68
|
-
"""Basic init."""
|
|
69
|
-
_LOGGER.debug(">> Init authenticator.")
|
|
70
|
-
self._at_expiry: datetime = None
|
|
71
|
-
self._access_token: str = None
|
|
72
|
-
self.available_scopes: dict = None
|
|
73
|
-
self.account_id: str = None
|
|
74
|
-
self.account: dict = None
|
|
75
|
-
self._auth_code_verifier: str = auth_code_verifier
|
|
76
|
-
self._refresh_token: str = None
|
|
77
|
-
self._id_token: str = None
|
|
78
|
-
self._session_token: str = session_token
|
|
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
|
|
83
|
-
|
|
84
|
-
@property
|
|
85
|
-
def get_session_token(self) -> str:
|
|
86
|
-
"""Return the session token."""
|
|
87
|
-
return self._session_token
|
|
88
|
-
|
|
89
|
-
@property
|
|
90
|
-
def access_token(self) -> str:
|
|
91
|
-
"""Return the formatted access token."""
|
|
92
|
-
return f"Bearer {self._id_token}" # v2 seems to use ID token for API access?
|
|
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
|
-
|
|
99
|
-
async def _request_handler(self, method, url, json=None, data=None, headers: dict=None):
|
|
100
|
-
"""Send a HTTP request"""
|
|
101
|
-
if headers is None:
|
|
102
|
-
headers = {}
|
|
103
|
-
response: dict = {
|
|
104
|
-
"status": 0,
|
|
105
|
-
"text": "",
|
|
106
|
-
"json": "",
|
|
107
|
-
"headers": ""
|
|
108
|
-
}
|
|
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
|
|
120
|
-
return response
|
|
121
|
-
|
|
122
|
-
def _read_tokens(self, tokens: dict):
|
|
123
|
-
"""Reads tokens into self."""
|
|
124
|
-
self.available_scopes = tokens.get("scope")
|
|
125
|
-
self._at_expiry = datetime.now() + timedelta(seconds=tokens.get("expires_in"))
|
|
126
|
-
self._id_token = tokens.get("id_token")
|
|
127
|
-
self._access_token = tokens.get("access_token")
|
|
128
|
-
|
|
129
|
-
async def perform_login(self, session_token_code):
|
|
130
|
-
"""Retrieves initial tokens."""
|
|
131
|
-
_LOGGER.debug("Performing initial login.")
|
|
132
|
-
session_token_form = aiohttp.FormData()
|
|
133
|
-
session_token_form.add_field("client_id", CLIENT_ID)
|
|
134
|
-
session_token_form.add_field("session_token_code", session_token_code)
|
|
135
|
-
session_token_form.add_field("session_token_code_verifier", self._auth_code_verifier)
|
|
136
|
-
session_token_response = await self._request_handler(
|
|
137
|
-
method="POST",
|
|
138
|
-
url=SESSION_TOKEN_URL,
|
|
139
|
-
data=session_token_form
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
if session_token_response.get("status") != 200:
|
|
143
|
-
raise HttpException(session_token_response.get("status"),
|
|
144
|
-
session_token_response.get("text"))
|
|
145
|
-
|
|
146
|
-
self._session_token = session_token_response["json"]["session_token"]
|
|
147
|
-
|
|
148
|
-
async def perform_refresh(self):
|
|
149
|
-
"""Refresh the access token."""
|
|
150
|
-
_LOGGER.debug("Refreshing access token.")
|
|
151
|
-
token_response = await self._request_handler(
|
|
152
|
-
method="POST",
|
|
153
|
-
url=TOKEN_URL,
|
|
154
|
-
json={
|
|
155
|
-
"client_id": CLIENT_ID,
|
|
156
|
-
"grant_type": GRANT_TYPE,
|
|
157
|
-
"session_token": self.get_session_token
|
|
158
|
-
}
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
if token_response["status"] == 400:
|
|
162
|
-
raise InvalidSessionTokenException(400, token_response["json"]["error"])
|
|
163
|
-
|
|
164
|
-
if token_response["status"] == 401:
|
|
165
|
-
raise InvalidOAuthConfigurationException(401, token_response["json"]["error"])
|
|
166
|
-
|
|
167
|
-
if token_response.get("status") != 200:
|
|
168
|
-
raise HttpException(token_response.get("status"), f"login error {token_response.get('status')}")
|
|
169
|
-
|
|
170
|
-
self._read_tokens(token_response.get("json"))
|
|
171
|
-
if self.account_id is None:
|
|
172
|
-
# fill account_id
|
|
173
|
-
account = await self._request_handler(
|
|
174
|
-
method="GET",
|
|
175
|
-
url=MY_ACCOUNT_ENDPOINT,
|
|
176
|
-
headers={
|
|
177
|
-
"Authorization": f"Bearer {self._access_token}"
|
|
178
|
-
}
|
|
179
|
-
)
|
|
180
|
-
if account["status"] != 200:
|
|
181
|
-
raise HttpException(account["status"], f"Unable to get account_id {account['status']}")
|
|
182
|
-
self.account_id = account["json"]["id"]
|
|
183
|
-
self.account = account["json"]
|
|
184
|
-
|
|
185
|
-
@classmethod
|
|
186
|
-
def generate_login(
|
|
187
|
-
cls,
|
|
188
|
-
client_session: aiohttp.ClientSession | None = None) -> 'Authenticator':
|
|
189
|
-
"""Starts configuration of the authenticator."""
|
|
190
|
-
verifier = _rand()
|
|
191
|
-
|
|
192
|
-
auth = cls(auth_code_verifier=verifier, client_session=client_session)
|
|
193
|
-
|
|
194
|
-
query = {
|
|
195
|
-
"client_id": CLIENT_ID,
|
|
196
|
-
# "interacted": 1,
|
|
197
|
-
"redirect_uri": REDIRECT_URI,
|
|
198
|
-
"response_type": "session_token_code",
|
|
199
|
-
"scope": "+".join(SCOPES),
|
|
200
|
-
"session_token_code_challenge": _hash(verifier),
|
|
201
|
-
"session_token_code_challenge_method": "S256",
|
|
202
|
-
"state": _rand(),
|
|
203
|
-
"theme": "login_form"
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
auth.login_url = AUTHORIZE_URL.format(urlencode(query)).replace("%2B", "+")
|
|
207
|
-
return auth
|
|
208
|
-
|
|
209
|
-
@classmethod
|
|
210
|
-
async def complete_login(cls,
|
|
211
|
-
auth: Authenticator | None,
|
|
212
|
-
response_token: str,
|
|
213
|
-
is_session_token: bool=False,
|
|
214
|
-
client_session: aiohttp.ClientSession | None = None) -> Authenticator:
|
|
215
|
-
"""Creates and logs into Nintendo APIs"""
|
|
216
|
-
if is_session_token:
|
|
217
|
-
auth = cls(session_token=response_token, client_session=client_session)
|
|
218
|
-
await auth.perform_refresh()
|
|
219
|
-
else:
|
|
220
|
-
response_token = _parse_response_token(response_token)
|
|
221
|
-
await auth.perform_login(
|
|
222
|
-
session_token_code=response_token.get("session_token_code")
|
|
223
|
-
)
|
|
224
|
-
await auth.perform_refresh()
|
|
225
|
-
|
|
226
|
-
return auth
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
# pylint: disable=line-too-long
|
|
2
|
-
"""Static values."""
|
|
3
|
-
|
|
4
|
-
CLIENT_ID = "54789befb391a838"
|
|
5
|
-
GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer-session-token"
|
|
6
|
-
|
|
7
|
-
REDIRECT_URI = f"npf{CLIENT_ID}://auth"
|
|
8
|
-
SCOPES = [
|
|
9
|
-
"openid",
|
|
10
|
-
"user",
|
|
11
|
-
"user.mii",
|
|
12
|
-
"moonUser:administration",
|
|
13
|
-
"moonDevice:create",
|
|
14
|
-
"moonOwnedDevice:administration",
|
|
15
|
-
"moonParentalControlSetting",
|
|
16
|
-
"moonParentalControlSetting:update",
|
|
17
|
-
"moonParentalControlSettingState",
|
|
18
|
-
"moonPairingState",
|
|
19
|
-
"moonSmartDevice:administration",
|
|
20
|
-
"moonDailySummary",
|
|
21
|
-
"moonMonthlySummary",
|
|
22
|
-
]
|
|
23
|
-
|
|
24
|
-
AUTHORIZE_URL = "https://accounts.nintendo.com/connect/1.0.0/authorize?{}"
|
|
25
|
-
SESSION_TOKEN_URL = "https://accounts.nintendo.com/connect/1.0.0/api/session_token"
|
|
26
|
-
TOKEN_URL = "https://accounts.nintendo.com/connect/1.0.0/api/token"
|
|
27
|
-
|
|
28
|
-
ACCOUNT_API_BASE = "https://api.accounts.nintendo.com/2.0.0"
|
|
29
|
-
MY_ACCOUNT_ENDPOINT = f"{ACCOUNT_API_BASE}/users/me"
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
pynintendoparental/__init__.py,sha256=pNcBsHRa4B85USP7uzwPEGF9fu3MA9YgW_hI82F_NXQ,2460
|
|
2
|
-
pynintendoparental/_version.py,sha256=5SgGjThsHu_ITn8V83BvCziqCwxdXxTQqcC3KQMHPfM,22
|
|
3
|
-
pynintendoparental/api.py,sha256=hMXq0eNIgFELlNZJtN0rK3plKyu9nirvwiUPNlkjOCY,7013
|
|
4
|
-
pynintendoparental/application.py,sha256=l-oVwM4hrVVUf_2djQ7rJVya7LQP38yhaLPAWt8V8TY,3941
|
|
5
|
-
pynintendoparental/const.py,sha256=sQZqU0f1NSClMPfCSJonlCunLdbPPiXjL-JS2LMZGd4,2101
|
|
6
|
-
pynintendoparental/device.py,sha256=EEQfnab96CIGlB2n1gePQBn5SWx7cck8tX_qz8xAHjc,22956
|
|
7
|
-
pynintendoparental/enum.py,sha256=lzacGti7fcQqAOROjB9782De7bOMYKSEM61SQd6aYG4,401
|
|
8
|
-
pynintendoparental/exceptions.py,sha256=-KuRBoMtzWKycjdgIsRYPzSVi_uzPjROq7blJSgq-iw,1450
|
|
9
|
-
pynintendoparental/player.py,sha256=WDl0pspHgrV9lGhDp-NKlfP8DV4Yxe02aYaGg9wTTeg,1785
|
|
10
|
-
pynintendoparental/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
|
-
pynintendoparental/utils.py,sha256=5-EP_rmPnSSWtbi18Y226GtjLhF3PLONKwmRdiy7m2c,198
|
|
12
|
-
pynintendoparental/authenticator/__init__.py,sha256=MZdA6qqHV0i7rspNL9Z9xl7aRy-EJEm3NIapiIgEJBA,7688
|
|
13
|
-
pynintendoparental/authenticator/const.py,sha256=_nUJVC0U64j_n1LaQd_KDg0EWFcezb87bQyYYXpbPPY,917
|
|
14
|
-
pynintendoparental-1.1.2.dist-info/licenses/LICENSE,sha256=zsxHgHVMnyWq121yND8zBl9Rl9H6EF2K9N51B2ZSm_k,1071
|
|
15
|
-
pynintendoparental-1.1.2.dist-info/METADATA,sha256=wV4_2Gwy86YLWZe8SOu5svDLpH2VWsi5ORPCVfYzLKk,1871
|
|
16
|
-
pynintendoparental-1.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
17
|
-
pynintendoparental-1.1.2.dist-info/top_level.txt,sha256=QQ5bAl-Ljso16P8KLf1NHrFmKk9jLT7bVJG_rVlIXWk,19
|
|
18
|
-
pynintendoparental-1.1.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|