pynintendoparental 1.1.2__tar.gz → 2.0.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.
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/PKG-INFO +3 -1
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/__init__.py +11 -13
- pynintendoparental-2.0.0/pynintendoparental/_version.py +1 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/api.py +68 -84
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/application.py +17 -11
- pynintendoparental-2.0.0/pynintendoparental/authenticator.py +18 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/const.py +21 -12
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/device.py +300 -204
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/enum.py +15 -1
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/exceptions.py +4 -17
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/player.py +3 -1
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/utils.py +1 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental.egg-info/PKG-INFO +3 -1
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental.egg-info/SOURCES.txt +2 -3
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental.egg-info/requires.txt +1 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/setup.py +1 -0
- pynintendoparental-1.1.2/pynintendoparental/_version.py +0 -1
- pynintendoparental-1.1.2/pynintendoparental/authenticator/__init__.py +0 -226
- pynintendoparental-1.1.2/pynintendoparental/authenticator/const.py +0 -29
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/LICENSE +0 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/README.md +0 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental/py.typed +0 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental.egg-info/dependency_links.txt +0 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pynintendoparental.egg-info/top_level.txt +0 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/pyproject.toml +0 -0
- {pynintendoparental-1.1.2 → pynintendoparental-2.0.0}/setup.cfg +0 -0
|
@@ -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
|
|
|
@@ -3,34 +3,33 @@
|
|
|
3
3
|
|
|
4
4
|
import asyncio
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from pynintendoauth.exceptions import HttpException
|
|
7
7
|
|
|
8
|
-
from .authenticator import Authenticator
|
|
9
8
|
from .api import Api
|
|
10
9
|
from .const import _LOGGER
|
|
11
10
|
from .device import Device
|
|
11
|
+
from .exceptions import NoDevicesFoundException
|
|
12
|
+
from .authenticator import Authenticator
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
class NintendoParental:
|
|
14
16
|
"""Core Python API."""
|
|
15
17
|
|
|
16
|
-
def __init__(self,
|
|
17
|
-
auth: Authenticator,
|
|
18
|
-
timezone,
|
|
19
|
-
lang) -> None:
|
|
18
|
+
def __init__(self, auth: Authenticator, timezone, lang) -> None:
|
|
20
19
|
self._api: Api = Api(auth=auth, tz=timezone, lang=lang)
|
|
21
20
|
self.account_id = auth.account_id
|
|
22
21
|
self.devices: dict[str, Device] = {}
|
|
23
22
|
|
|
24
23
|
async def _get_devices(self):
|
|
25
24
|
"""Gets devices from the API and stores in self.devices"""
|
|
25
|
+
|
|
26
26
|
async def update_device(dev: Device):
|
|
27
27
|
"""Update a device."""
|
|
28
28
|
try:
|
|
29
29
|
await dev.update()
|
|
30
30
|
except Exception as err:
|
|
31
|
-
_LOGGER.exception("Error updating device %s: %s",
|
|
32
|
-
|
|
33
|
-
err)
|
|
31
|
+
_LOGGER.exception("Error updating device %s: %s", dev.device_id, err)
|
|
32
|
+
|
|
34
33
|
try:
|
|
35
34
|
response = await self._api.async_get_account_devices()
|
|
36
35
|
except HttpException as err:
|
|
@@ -55,10 +54,9 @@ class NintendoParental:
|
|
|
55
54
|
_LOGGER.debug("Update complete.")
|
|
56
55
|
|
|
57
56
|
@classmethod
|
|
58
|
-
async def create(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
lang: str = "en-GB") -> 'NintendoParental':
|
|
57
|
+
async def create(
|
|
58
|
+
cls, auth: Authenticator, timezone: str = "Europe/London", lang: str = "en-GB"
|
|
59
|
+
) -> "NintendoParental":
|
|
62
60
|
"""Create an instance of NintendoParental."""
|
|
63
61
|
self = cls(auth, timezone, lang)
|
|
64
62
|
await self.update()
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "2.0.0"
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import aiohttp
|
|
4
4
|
|
|
5
|
+
from pynintendoauth.exceptions import HttpException
|
|
6
|
+
|
|
5
7
|
from .authenticator import Authenticator
|
|
6
8
|
from .const import (
|
|
7
9
|
ENDPOINTS,
|
|
@@ -13,13 +15,14 @@ from .const import (
|
|
|
13
15
|
OS_VERSION,
|
|
14
16
|
OS_NAME,
|
|
15
17
|
DEVICE_MODEL,
|
|
16
|
-
_LOGGER
|
|
18
|
+
_LOGGER,
|
|
17
19
|
)
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
def _check_http_success(status: int) -> bool:
|
|
21
23
|
return status >= 200 and status < 300
|
|
22
24
|
|
|
25
|
+
|
|
23
26
|
class Api:
|
|
24
27
|
"""Nintendo Parental Controls API."""
|
|
25
28
|
|
|
@@ -48,10 +51,10 @@ class Api:
|
|
|
48
51
|
"X-Moon-TimeZone": self._tz,
|
|
49
52
|
"X-Moon-Os-Language": self._language,
|
|
50
53
|
"X-Moon-App-Language": self._language,
|
|
51
|
-
"Authorization": self._auth.access_token
|
|
54
|
+
"Authorization": self._auth.access_token,
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
async def send_request(self, endpoint: str, body: object=None, **kwargs):
|
|
57
|
+
async def send_request(self, endpoint: str, body: object = None, **kwargs):
|
|
55
58
|
"""Sends a request to a given endpoint."""
|
|
56
59
|
_LOGGER.debug("Sending request to %s", endpoint)
|
|
57
60
|
# Get the endpoint from the endpoints map
|
|
@@ -66,38 +69,45 @@ class Api:
|
|
|
66
69
|
url = e_point.get("url").format(BASE_URL=BASE_URL, **kwargs)
|
|
67
70
|
_LOGGER.debug("Built URL %s", url)
|
|
68
71
|
# now send the HTTP request
|
|
69
|
-
resp: dict = {
|
|
70
|
-
|
|
71
|
-
"
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
_LOGGER.debug("%s request to %s status code %s",
|
|
82
|
-
e_point.get("method"),
|
|
83
|
-
url,
|
|
84
|
-
response.status)
|
|
85
|
-
if _check_http_success(response.status):
|
|
86
|
-
resp["status"] = response.status
|
|
87
|
-
resp["text"] = await response.text()
|
|
72
|
+
resp: dict = {"status": 0, "text": "", "json": "", "headers": ""}
|
|
73
|
+
response = await self._auth.async_authenticated_request(
|
|
74
|
+
method=e_point.get("method"), url=url, headers=self._headers, body=body
|
|
75
|
+
)
|
|
76
|
+
_LOGGER.debug(
|
|
77
|
+
"%s request to %s status code %s",
|
|
78
|
+
e_point.get("method"),
|
|
79
|
+
url,
|
|
80
|
+
response.status,
|
|
81
|
+
)
|
|
82
|
+
if not _check_http_success(response.status):
|
|
83
|
+
if response.content_type == "application/problem+json":
|
|
88
84
|
try:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
85
|
+
error: dict = await response.json()
|
|
86
|
+
if "detail" in error:
|
|
87
|
+
raise HttpException(
|
|
88
|
+
response.status, error["detail"], error.get("errorCode")
|
|
89
|
+
)
|
|
90
|
+
except (aiohttp.ContentTypeError, ValueError):
|
|
91
|
+
# Fall through to the generic exception below on parsing failure.
|
|
92
|
+
pass
|
|
93
|
+
raise HttpException(response.status, await response.text())
|
|
94
|
+
|
|
95
|
+
resp["status"] = response.status
|
|
96
|
+
resp["text"] = await response.text()
|
|
97
|
+
try:
|
|
98
|
+
resp["json"] = await response.json()
|
|
99
|
+
except (aiohttp.ContentTypeError, ValueError) as e:
|
|
100
|
+
_LOGGER.warning(
|
|
101
|
+
"""Failed to decode JSON response from %s.
|
|
102
|
+
Status: %s, Error: %s.
|
|
103
|
+
Response text: %s...""",
|
|
104
|
+
url,
|
|
105
|
+
response.status,
|
|
106
|
+
e,
|
|
107
|
+
resp["text"][:200],
|
|
108
|
+
)
|
|
109
|
+
resp["json"] = {}
|
|
110
|
+
resp["headers"] = response.headers
|
|
101
111
|
|
|
102
112
|
# now return the resp dict
|
|
103
113
|
return resp
|
|
@@ -105,109 +115,83 @@ class Api:
|
|
|
105
115
|
async def async_get_account_details(self) -> dict:
|
|
106
116
|
"""Get account details."""
|
|
107
117
|
return await self.send_request(
|
|
108
|
-
endpoint="get_account_details",
|
|
109
|
-
ACCOUNT_ID=self.account_id
|
|
118
|
+
endpoint="get_account_details", ACCOUNT_ID=self.account_id
|
|
110
119
|
)
|
|
111
120
|
|
|
112
121
|
async def async_get_account_devices(self) -> dict:
|
|
113
122
|
"""Get account devices."""
|
|
114
|
-
return await self.send_request(
|
|
115
|
-
endpoint="get_account_devices"
|
|
116
|
-
)
|
|
123
|
+
return await self.send_request(endpoint="get_account_devices")
|
|
117
124
|
|
|
118
125
|
async def async_get_account_device(self, device_id: str) -> dict:
|
|
119
126
|
"""Get account device."""
|
|
120
127
|
return await self.send_request(
|
|
121
|
-
endpoint="get_account_device",
|
|
122
|
-
DEVICE_ID=device_id
|
|
128
|
+
endpoint="get_account_device", DEVICE_ID=device_id
|
|
123
129
|
)
|
|
124
130
|
|
|
125
131
|
async def async_get_device_daily_summaries(self, device_id: str) -> dict:
|
|
126
132
|
"""Get device daily summaries."""
|
|
127
133
|
return await self.send_request(
|
|
128
|
-
endpoint="get_device_daily_summaries",
|
|
129
|
-
DEVICE_ID=device_id
|
|
134
|
+
endpoint="get_device_daily_summaries", DEVICE_ID=device_id
|
|
130
135
|
)
|
|
131
136
|
|
|
132
137
|
async def async_get_device_monthly_summaries(self, device_id: str) -> dict:
|
|
133
138
|
"""Get device monthly summaries."""
|
|
134
139
|
return await self.send_request(
|
|
135
|
-
endpoint="get_device_monthly_summaries",
|
|
136
|
-
DEVICE_ID=device_id
|
|
140
|
+
endpoint="get_device_monthly_summaries", DEVICE_ID=device_id
|
|
137
141
|
)
|
|
138
142
|
|
|
139
143
|
async def async_get_device_parental_control_setting(self, device_id: str) -> dict:
|
|
140
144
|
"""Get device parental control setting."""
|
|
141
145
|
return await self.send_request(
|
|
142
|
-
endpoint="get_device_parental_control_setting",
|
|
143
|
-
DEVICE_ID=device_id
|
|
146
|
+
endpoint="get_device_parental_control_setting", DEVICE_ID=device_id
|
|
144
147
|
)
|
|
145
148
|
|
|
146
|
-
async def async_get_device_parental_control_setting_state(
|
|
149
|
+
async def async_get_device_parental_control_setting_state(
|
|
150
|
+
self, device_id: str
|
|
151
|
+
) -> dict:
|
|
147
152
|
"""Get device parental control setting state."""
|
|
148
153
|
return await self.send_request(
|
|
149
|
-
endpoint="get_device_parental_control_setting_state",
|
|
150
|
-
DEVICE_ID=device_id
|
|
154
|
+
endpoint="get_device_parental_control_setting_state", DEVICE_ID=device_id
|
|
151
155
|
)
|
|
152
156
|
|
|
153
|
-
async def async_get_device_monthly_summary(
|
|
157
|
+
async def async_get_device_monthly_summary(
|
|
158
|
+
self, device_id: str, year: int, month: int
|
|
159
|
+
) -> dict:
|
|
154
160
|
"""Get device monthly summary."""
|
|
155
161
|
return await self.send_request(
|
|
156
162
|
endpoint="get_device_monthly_summary",
|
|
157
163
|
DEVICE_ID=device_id,
|
|
158
164
|
YEAR=year,
|
|
159
|
-
MONTH=f"{month:02d}"
|
|
165
|
+
MONTH=f"{month:02d}",
|
|
160
166
|
)
|
|
161
167
|
|
|
162
|
-
async def async_update_restriction_level(
|
|
163
|
-
self,
|
|
164
|
-
settings: dict
|
|
165
|
-
) -> dict:
|
|
168
|
+
async def async_update_restriction_level(self, settings: dict) -> dict:
|
|
166
169
|
"""Update device restriction level."""
|
|
167
170
|
return await self.send_request(
|
|
168
|
-
endpoint="update_restriction_level",
|
|
169
|
-
body=settings
|
|
171
|
+
endpoint="update_restriction_level", body=settings
|
|
170
172
|
)
|
|
171
173
|
|
|
172
|
-
async def async_update_play_timer(
|
|
173
|
-
self,
|
|
174
|
-
settings: dict
|
|
175
|
-
) -> dict:
|
|
174
|
+
async def async_update_play_timer(self, settings: dict) -> dict:
|
|
176
175
|
"""Update device play timer settings."""
|
|
177
|
-
return await self.send_request(
|
|
178
|
-
endpoint="update_play_timer",
|
|
179
|
-
body=settings
|
|
180
|
-
)
|
|
176
|
+
return await self.send_request(endpoint="update_play_timer", body=settings)
|
|
181
177
|
|
|
182
|
-
async def async_update_unlock_code(
|
|
183
|
-
self,
|
|
184
|
-
new_code: str,
|
|
185
|
-
device_id: str
|
|
186
|
-
) -> dict:
|
|
178
|
+
async def async_update_unlock_code(self, new_code: str, device_id: str) -> dict:
|
|
187
179
|
"""Update device unlock code."""
|
|
188
180
|
return await self.send_request(
|
|
189
181
|
endpoint="update_unlock_code",
|
|
190
|
-
body={
|
|
191
|
-
"deviceId": device_id,
|
|
192
|
-
"unlockCode": new_code
|
|
193
|
-
}
|
|
182
|
+
body={"deviceId": device_id, "unlockCode": new_code},
|
|
194
183
|
)
|
|
195
184
|
|
|
196
185
|
async def async_update_extra_playing_time(
|
|
197
|
-
self,
|
|
198
|
-
device_id: str,
|
|
199
|
-
additional_time: int
|
|
186
|
+
self, device_id: str, additional_time: int
|
|
200
187
|
) -> dict:
|
|
201
188
|
"""Update device extra playing time."""
|
|
202
189
|
body = {
|
|
203
190
|
"deviceId": device_id,
|
|
204
191
|
"additionalTime": additional_time,
|
|
205
|
-
"status": "TO_ADDED"
|
|
192
|
+
"status": "TO_ADDED",
|
|
206
193
|
}
|
|
207
194
|
if additional_time == -1:
|
|
208
195
|
body["status"] = "TO_INFINITY"
|
|
209
196
|
body.pop("additionalTime")
|
|
210
|
-
return await self.send_request(
|
|
211
|
-
endpoint="update_extra_playing_time",
|
|
212
|
-
body=body
|
|
213
|
-
)
|
|
197
|
+
return await self.send_request(endpoint="update_extra_playing_time", body=body)
|
|
@@ -4,6 +4,7 @@ from datetime import datetime
|
|
|
4
4
|
|
|
5
5
|
from .const import _LOGGER
|
|
6
6
|
|
|
7
|
+
|
|
7
8
|
class Application:
|
|
8
9
|
"""Model for an application"""
|
|
9
10
|
|
|
@@ -12,7 +13,7 @@ class Application:
|
|
|
12
13
|
self.application_id: str = None
|
|
13
14
|
self.first_played_date: datetime = None
|
|
14
15
|
self.has_ugc: bool = None
|
|
15
|
-
self.image_url: str = None
|
|
16
|
+
self.image_url: str = None # uses small image from Nintendo
|
|
16
17
|
self.playing_days: int = None
|
|
17
18
|
self.shop_url: str = None
|
|
18
19
|
self.name: str = None
|
|
@@ -20,11 +21,10 @@ class Application:
|
|
|
20
21
|
|
|
21
22
|
def update_today_time_played(self, daily_summary: dict):
|
|
22
23
|
"""Updates the today time played for the given application."""
|
|
23
|
-
_LOGGER.debug("Updating today time played for app %s",
|
|
24
|
-
self.application_id)
|
|
24
|
+
_LOGGER.debug("Updating today time played for app %s", self.application_id)
|
|
25
25
|
self.today_time_played = daily_summary.get("playingTime", 0)
|
|
26
26
|
|
|
27
|
-
def update(self, updated:
|
|
27
|
+
def update(self, updated: "Application"):
|
|
28
28
|
"""Updates self with a given application."""
|
|
29
29
|
_LOGGER.debug("Updating application %s", self.application_id)
|
|
30
30
|
self.application_id = updated.application_id
|
|
@@ -37,7 +37,7 @@ class Application:
|
|
|
37
37
|
self.today_time_played = updated.today_time_played
|
|
38
38
|
|
|
39
39
|
@classmethod
|
|
40
|
-
def from_daily_summary(cls, raw: list) -> list[
|
|
40
|
+
def from_daily_summary(cls, raw: list) -> list["Application"]:
|
|
41
41
|
"""Converts a raw daily summary response into a list of applications."""
|
|
42
42
|
built = []
|
|
43
43
|
if "playedApps" in raw:
|
|
@@ -49,7 +49,7 @@ class Application:
|
|
|
49
49
|
return built
|
|
50
50
|
|
|
51
51
|
@staticmethod
|
|
52
|
-
def check_if_app_in_list(app_list: list[
|
|
52
|
+
def check_if_app_in_list(app_list: list["Application"], app: "Application") -> bool:
|
|
53
53
|
"""Checks if an app is in a list."""
|
|
54
54
|
for app_li in app_list:
|
|
55
55
|
if app_li.application_id == app.application_id:
|
|
@@ -57,7 +57,9 @@ class Application:
|
|
|
57
57
|
return False
|
|
58
58
|
|
|
59
59
|
@staticmethod
|
|
60
|
-
def return_app_from_list(
|
|
60
|
+
def return_app_from_list(
|
|
61
|
+
app_list: list["Application"], application_id: str
|
|
62
|
+
) -> "Application":
|
|
61
63
|
"""Returns a single app from a given list."""
|
|
62
64
|
for app in app_list:
|
|
63
65
|
if app.application_id == application_id:
|
|
@@ -65,28 +67,32 @@ class Application:
|
|
|
65
67
|
return None
|
|
66
68
|
|
|
67
69
|
@classmethod
|
|
68
|
-
def from_whitelist(cls, raw: dict) -> list[
|
|
70
|
+
def from_whitelist(cls, raw: dict) -> list["Application"]:
|
|
69
71
|
"""Converts a raw whitelist response into a list of applications."""
|
|
70
72
|
parsed = []
|
|
71
73
|
for app_id in raw:
|
|
72
74
|
_LOGGER.debug("Parsing app %s", app_id)
|
|
73
75
|
internal = cls()
|
|
74
76
|
internal.application_id = raw[app_id]["applicationId"]
|
|
75
|
-
internal.first_played_date = datetime.strptime(
|
|
77
|
+
internal.first_played_date = datetime.strptime(
|
|
78
|
+
raw[app_id]["firstPlayDate"], "%Y-%m-%d"
|
|
79
|
+
)
|
|
76
80
|
internal.image_url = raw[app_id]["imageUri"]
|
|
77
81
|
internal.name = raw[app_id]["title"]
|
|
78
82
|
parsed.append(internal)
|
|
79
83
|
return parsed
|
|
80
84
|
|
|
81
85
|
@classmethod
|
|
82
|
-
def from_monthly_summary(cls, raw: list) -> list[
|
|
86
|
+
def from_monthly_summary(cls, raw: list) -> list["Application"]:
|
|
83
87
|
"""Converts a raw monthly summary response into a list of applications."""
|
|
84
88
|
parsed = []
|
|
85
89
|
for app in raw:
|
|
86
90
|
_LOGGER.debug("Parsing app %s", app)
|
|
87
91
|
internal = cls()
|
|
88
92
|
internal.application_id = app.get("applicationId").capitalize()
|
|
89
|
-
internal.first_played_date = datetime.strptime(
|
|
93
|
+
internal.first_played_date = datetime.strptime(
|
|
94
|
+
app.get("firstPlayDate"), "%Y-%m-%d"
|
|
95
|
+
)
|
|
90
96
|
internal.has_ugc = app.get("hasUgc", False)
|
|
91
97
|
internal.image_url = app.get("imageUri").get("small")
|
|
92
98
|
internal.playing_days = app.get("playingDays", None)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Nintendo Authentication."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pynintendoauth import NintendoAuth
|
|
6
|
+
|
|
7
|
+
from .const import CLIENT_ID
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class Authenticator(NintendoAuth):
|
|
11
|
+
"""Authentication functions."""
|
|
12
|
+
|
|
13
|
+
def __init__(self, session_token=None, client_session=None):
|
|
14
|
+
super().__init__(
|
|
15
|
+
client_id=CLIENT_ID,
|
|
16
|
+
session_token=session_token,
|
|
17
|
+
client_session=client_session,
|
|
18
|
+
)
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import logging
|
|
5
5
|
|
|
6
6
|
_LOGGER = logging.getLogger(__package__)
|
|
7
|
+
CLIENT_ID = "54789befb391a838"
|
|
7
8
|
MOBILE_APP_PKG = "com.nintendo.znma"
|
|
8
9
|
MOBILE_APP_VERSION = "2.2.0"
|
|
9
10
|
MOBILE_APP_BUILD = "560"
|
|
@@ -14,47 +15,55 @@ DEVICE_MODEL = "Pixel 4 XL"
|
|
|
14
15
|
BASE_URL = "https://app.lp1.znma.srv.nintendo.net/v2"
|
|
15
16
|
USER_AGENT = f"moon_ANDROID/{MOBILE_APP_VERSION} ({MOBILE_APP_PKG}; build:{MOBILE_APP_BUILD}; {OS_STR})"
|
|
16
17
|
|
|
17
|
-
DAYS_OF_WEEK = [
|
|
18
|
+
DAYS_OF_WEEK = [
|
|
19
|
+
"monday",
|
|
20
|
+
"tuesday",
|
|
21
|
+
"wednesday",
|
|
22
|
+
"thursday",
|
|
23
|
+
"friday",
|
|
24
|
+
"saturday",
|
|
25
|
+
"sunday",
|
|
26
|
+
]
|
|
18
27
|
|
|
19
28
|
ENDPOINTS = {
|
|
20
29
|
"get_account_devices": {
|
|
21
30
|
"url": "{BASE_URL}/actions/user/fetchOwnedDevices",
|
|
22
|
-
"method": "GET"
|
|
31
|
+
"method": "GET",
|
|
23
32
|
},
|
|
24
33
|
"get_account_device": {
|
|
25
34
|
"url": "{BASE_URL}/actions/user/fetchOwnedDevice?deviceId={DEVICE_ID}",
|
|
26
|
-
"method": "GET"
|
|
35
|
+
"method": "GET",
|
|
27
36
|
},
|
|
28
37
|
"get_device_daily_summaries": {
|
|
29
38
|
"url": "{BASE_URL}/actions/playSummary/fetchDailySummaries?deviceId={DEVICE_ID}",
|
|
30
|
-
"method": "GET"
|
|
39
|
+
"method": "GET",
|
|
31
40
|
},
|
|
32
41
|
"get_device_monthly_summaries": {
|
|
33
42
|
"url": "{BASE_URL}/actions/playSummary/fetchLatestMonthlySummary?deviceId={DEVICE_ID}",
|
|
34
|
-
"method": "GET"
|
|
43
|
+
"method": "GET",
|
|
35
44
|
},
|
|
36
45
|
"get_device_parental_control_setting": {
|
|
37
46
|
"url": "{BASE_URL}/actions/parentalControlSetting/fetchParentalControlSetting?deviceId={DEVICE_ID}",
|
|
38
|
-
"method": "GET"
|
|
47
|
+
"method": "GET",
|
|
39
48
|
},
|
|
40
49
|
"update_restriction_level": {
|
|
41
50
|
"url": "{BASE_URL}/actions/parentalControlSetting/updateRestrictionLevel",
|
|
42
|
-
"method": "POST"
|
|
51
|
+
"method": "POST",
|
|
43
52
|
},
|
|
44
53
|
"update_play_timer": {
|
|
45
54
|
"url": "{BASE_URL}/actions/parentalControlSetting/updatePlayTimer",
|
|
46
|
-
"method": "POST"
|
|
55
|
+
"method": "POST",
|
|
47
56
|
},
|
|
48
57
|
"update_unlock_code": {
|
|
49
58
|
"url": "{BASE_URL}/actions/parentalControlSetting/updateUnlockCode",
|
|
50
|
-
"method": "POST"
|
|
59
|
+
"method": "POST",
|
|
51
60
|
},
|
|
52
61
|
"get_device_monthly_summary": {
|
|
53
62
|
"url": "{BASE_URL}/actions/playSummary/fetchMonthlySummary?deviceId={DEVICE_ID}&year={YEAR}&month={MONTH}&containLatest=false",
|
|
54
|
-
"method": "GET"
|
|
63
|
+
"method": "GET",
|
|
55
64
|
},
|
|
56
65
|
"update_extra_playing_time": {
|
|
57
66
|
"url": "{BASE_URL}/actions/device/updateExtraPlayingTime",
|
|
58
|
-
"method": "POST"
|
|
59
|
-
}
|
|
67
|
+
"method": "POST",
|
|
68
|
+
},
|
|
60
69
|
}
|