pynintendoparental 1.1.3__tar.gz → 2.1.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.3 → pynintendoparental-2.1.0}/PKG-INFO +8 -2
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/__init__.py +11 -13
- pynintendoparental-2.1.0/pynintendoparental/_version.py +1 -0
- pynintendoparental-2.1.0/pynintendoparental/api.py +183 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/application.py +17 -11
- pynintendoparental-2.1.0/pynintendoparental/authenticator.py +18 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/const.py +21 -12
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/device.py +219 -109
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/enum.py +6 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/exceptions.py +5 -19
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/player.py +20 -9
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/utils.py +1 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental.egg-info/PKG-INFO +8 -2
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental.egg-info/SOURCES.txt +5 -2
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental.egg-info/requires.txt +6 -1
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/setup.py +6 -1
- pynintendoparental-2.1.0/tests/test_api.py +193 -0
- pynintendoparental-2.1.0/tests/test_device.py +110 -0
- pynintendoparental-2.1.0/tests/test_init.py +80 -0
- pynintendoparental-2.1.0/tests/test_player.py +46 -0
- pynintendoparental-1.1.3/pynintendoparental/_version.py +0 -1
- pynintendoparental-1.1.3/pynintendoparental/api.py +0 -221
- pynintendoparental-1.1.3/pynintendoparental/authenticator/__init__.py +0 -226
- pynintendoparental-1.1.3/pynintendoparental/authenticator/const.py +0 -29
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/LICENSE +0 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/README.md +0 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental/py.typed +0 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental.egg-info/dependency_links.txt +0 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pynintendoparental.egg-info/top_level.txt +0 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/pyproject.toml +0 -0
- {pynintendoparental-1.1.3 → pynintendoparental-2.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pynintendoparental
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.1.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,15 +11,20 @@ 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
|
|
16
|
+
Requires-Dist: aiofiles<26,>=23; extra == "dev"
|
|
15
17
|
Requires-Dist: bandit<1.9,>=1.7; extra == "dev"
|
|
16
18
|
Requires-Dist: black<26,>=23; extra == "dev"
|
|
17
19
|
Requires-Dist: build<1.4,>=0.10; extra == "dev"
|
|
20
|
+
Requires-Dist: Faker<39,>=38; extra == "dev"
|
|
18
21
|
Requires-Dist: flake8<8,>=6; extra == "dev"
|
|
19
22
|
Requires-Dist: isort<7,>=5; extra == "dev"
|
|
20
|
-
Requires-Dist: mypy<1.
|
|
23
|
+
Requires-Dist: mypy<1.20,>=1.5; extra == "dev"
|
|
21
24
|
Requires-Dist: pytest<9,>=7; extra == "dev"
|
|
22
25
|
Requires-Dist: pytest-cov<8,>=4; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-asyncio<1.0,>=0.21; extra == "dev"
|
|
27
|
+
Requires-Dist: syrupy<6,>=5; extra == "dev"
|
|
23
28
|
Requires-Dist: twine<7,>=4; extra == "dev"
|
|
24
29
|
Dynamic: author
|
|
25
30
|
Dynamic: classifier
|
|
@@ -29,6 +34,7 @@ Dynamic: home-page
|
|
|
29
34
|
Dynamic: license
|
|
30
35
|
Dynamic: license-file
|
|
31
36
|
Dynamic: provides-extra
|
|
37
|
+
Dynamic: requires-dist
|
|
32
38
|
Dynamic: requires-python
|
|
33
39
|
Dynamic: summary
|
|
34
40
|
|
|
@@ -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.1.0"
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""API handler."""
|
|
2
|
+
|
|
3
|
+
import aiohttp
|
|
4
|
+
|
|
5
|
+
from pynintendoauth.exceptions import HttpException
|
|
6
|
+
|
|
7
|
+
from .authenticator import Authenticator
|
|
8
|
+
from .const import (
|
|
9
|
+
ENDPOINTS,
|
|
10
|
+
BASE_URL,
|
|
11
|
+
USER_AGENT,
|
|
12
|
+
MOBILE_APP_PKG,
|
|
13
|
+
MOBILE_APP_BUILD,
|
|
14
|
+
MOBILE_APP_VERSION,
|
|
15
|
+
OS_VERSION,
|
|
16
|
+
OS_NAME,
|
|
17
|
+
DEVICE_MODEL,
|
|
18
|
+
_LOGGER,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _check_http_success(status: int) -> bool:
|
|
23
|
+
return status >= 200 and status < 300
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Api:
|
|
27
|
+
"""Nintendo Parental Controls API."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, auth, tz, lang):
|
|
30
|
+
"""INIT"""
|
|
31
|
+
self._auth: Authenticator = auth
|
|
32
|
+
self._tz = tz
|
|
33
|
+
self._language = lang
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def account_id(self):
|
|
37
|
+
"""Return the account id."""
|
|
38
|
+
return self._auth.account_id
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def _headers(self) -> dict:
|
|
42
|
+
"""Return web request headers."""
|
|
43
|
+
return {
|
|
44
|
+
"User-Agent": USER_AGENT,
|
|
45
|
+
"X-Moon-App-Id": MOBILE_APP_PKG,
|
|
46
|
+
"X-Moon-Os": OS_NAME,
|
|
47
|
+
"X-Moon-Os-Version": OS_VERSION,
|
|
48
|
+
"X-Moon-Model": DEVICE_MODEL,
|
|
49
|
+
"X-Moon-App-Display-Version": MOBILE_APP_VERSION,
|
|
50
|
+
"X-Moon-App-Internal-Version": MOBILE_APP_BUILD,
|
|
51
|
+
"X-Moon-TimeZone": self._tz,
|
|
52
|
+
"X-Moon-Os-Language": self._language,
|
|
53
|
+
"X-Moon-App-Language": self._language,
|
|
54
|
+
"Authorization": self._auth.access_token,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async def send_request(self, endpoint: str, body: object = None, **kwargs):
|
|
58
|
+
"""Sends a request to a given endpoint."""
|
|
59
|
+
_LOGGER.debug("Sending request to %s", endpoint)
|
|
60
|
+
# Get the endpoint from the endpoints map
|
|
61
|
+
e_point = ENDPOINTS.get(endpoint, None)
|
|
62
|
+
if e_point is None:
|
|
63
|
+
raise ValueError("Endpoint does not exist")
|
|
64
|
+
# refresh the token if it has expired.
|
|
65
|
+
if self._auth.access_token_expired:
|
|
66
|
+
_LOGGER.debug("Access token expired, requesting refresh.")
|
|
67
|
+
await self._auth.perform_refresh()
|
|
68
|
+
# format the URL using the kwargs
|
|
69
|
+
url = e_point.get("url").format(BASE_URL=BASE_URL, **kwargs)
|
|
70
|
+
_LOGGER.debug("Built URL %s", url)
|
|
71
|
+
# now send the HTTP request
|
|
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":
|
|
84
|
+
try:
|
|
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
|
|
111
|
+
|
|
112
|
+
# now return the resp dict
|
|
113
|
+
return resp
|
|
114
|
+
|
|
115
|
+
async def async_get_account_devices(self) -> dict:
|
|
116
|
+
"""Get account devices."""
|
|
117
|
+
return await self.send_request(endpoint="get_account_devices")
|
|
118
|
+
|
|
119
|
+
async def async_get_account_device(self, device_id: str) -> dict:
|
|
120
|
+
"""Get account device."""
|
|
121
|
+
return await self.send_request(
|
|
122
|
+
endpoint="get_account_device", DEVICE_ID=device_id
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
async def async_get_device_daily_summaries(self, device_id: str) -> dict:
|
|
126
|
+
"""Get device daily summaries."""
|
|
127
|
+
return await self.send_request(
|
|
128
|
+
endpoint="get_device_daily_summaries", DEVICE_ID=device_id
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
async def async_get_device_monthly_summaries(self, device_id: str) -> dict:
|
|
132
|
+
"""Get device monthly summaries."""
|
|
133
|
+
return await self.send_request(
|
|
134
|
+
endpoint="get_device_monthly_summaries", DEVICE_ID=device_id
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
async def async_get_device_parental_control_setting(self, device_id: str) -> dict:
|
|
138
|
+
"""Get device parental control setting."""
|
|
139
|
+
return await self.send_request(
|
|
140
|
+
endpoint="get_device_parental_control_setting", DEVICE_ID=device_id
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
async def async_get_device_monthly_summary(
|
|
144
|
+
self, device_id: str, year: int, month: int
|
|
145
|
+
) -> dict:
|
|
146
|
+
"""Get device monthly summary."""
|
|
147
|
+
return await self.send_request(
|
|
148
|
+
endpoint="get_device_monthly_summary",
|
|
149
|
+
DEVICE_ID=device_id,
|
|
150
|
+
YEAR=year,
|
|
151
|
+
MONTH=f"{month:02d}",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
async def async_update_restriction_level(self, settings: dict) -> dict:
|
|
155
|
+
"""Update device restriction level."""
|
|
156
|
+
return await self.send_request(
|
|
157
|
+
endpoint="update_restriction_level", body=settings
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
async def async_update_play_timer(self, settings: dict) -> dict:
|
|
161
|
+
"""Update device play timer settings."""
|
|
162
|
+
return await self.send_request(endpoint="update_play_timer", body=settings)
|
|
163
|
+
|
|
164
|
+
async def async_update_unlock_code(self, new_code: str, device_id: str) -> dict:
|
|
165
|
+
"""Update device unlock code."""
|
|
166
|
+
return await self.send_request(
|
|
167
|
+
endpoint="update_unlock_code",
|
|
168
|
+
body={"deviceId": device_id, "unlockCode": new_code},
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
async def async_update_extra_playing_time(
|
|
172
|
+
self, device_id: str, additional_time: int
|
|
173
|
+
) -> dict:
|
|
174
|
+
"""Update device extra playing time."""
|
|
175
|
+
body = {
|
|
176
|
+
"deviceId": device_id,
|
|
177
|
+
"additionalTime": additional_time,
|
|
178
|
+
"status": "TO_ADDED",
|
|
179
|
+
}
|
|
180
|
+
if additional_time == -1:
|
|
181
|
+
body["status"] = "TO_INFINITY"
|
|
182
|
+
body.pop("additionalTime")
|
|
183
|
+
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
|
}
|