MouseTools 3.1.0.dev2__tar.gz → 4.0.0b0__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.
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/LICENSE +1 -1
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0/MouseTools.egg-info}/PKG-INFO +5 -3
- mousetools-4.0.0b0/MouseTools.egg-info/SOURCES.txt +22 -0
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/MouseTools.egg-info/requires.txt +1 -0
- {mousetools-3.1.0.dev2/MouseTools.egg-info → mousetools-4.0.0b0}/PKG-INFO +5 -3
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/mousetools/__init__.py +1 -1
- mousetools-4.0.0b0/mousetools/api/menu.py +433 -0
- mousetools-4.0.0b0/mousetools/auth.py +112 -0
- mousetools-4.0.0b0/mousetools/channels/__init__.py +46 -0
- mousetools-4.0.0b0/mousetools/channels/enums.py +67 -0
- mousetools-4.0.0b0/mousetools/channels/facilities/__init__.py +520 -0
- mousetools-4.0.0b0/mousetools/channels/facilities/enums.py +30 -0
- mousetools-4.0.0b0/mousetools/channels/facilitystatus/__init__.py +153 -0
- mousetools-4.0.0b0/mousetools/mixins/__init__.py +0 -0
- mousetools-4.0.0b0/mousetools/mixins/couchbase.py +80 -0
- mousetools-4.0.0b0/mousetools/mixins/disney.py +36 -0
- mousetools-4.0.0b0/mousetools/resources/environments.bin +0 -0
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/pyproject.toml +19 -45
- mousetools-4.0.0b0/tests/test_mixins.py +24 -0
- mousetools-3.1.0.dev2/MouseTools.egg-info/SOURCES.txt +0 -38
- mousetools-3.1.0.dev2/mousetools/auth.py +0 -70
- mousetools-3.1.0.dev2/mousetools/entities/__init__.py +0 -43
- mousetools-3.1.0.dev2/mousetools/entities/attraction.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/base.py +0 -488
- mousetools-3.1.0.dev2/mousetools/entities/character.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/destination.py +0 -16
- mousetools-3.1.0.dev2/mousetools/entities/dining_event.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/dinner_show.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/entertainment.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/entertainment_venue.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/event.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/guest_services.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/land.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/menu.py +0 -226
- mousetools-3.1.0.dev2/mousetools/entities/merchandise_facility.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/park.py +0 -42
- mousetools-3.1.0.dev2/mousetools/entities/point_of_interest.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/resort.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/resort_area.py +0 -12
- mousetools-3.1.0.dev2/mousetools/entities/restaurant.py +0 -53
- mousetools-3.1.0.dev2/mousetools/exceptions.py +0 -17
- mousetools-3.1.0.dev2/mousetools/ids.py +0 -96
- mousetools-3.1.0.dev2/mousetools/mixins/disney.py +0 -55
- mousetools-3.1.0.dev2/mousetools/mixins/themeparksapi.py +0 -128
- mousetools-3.1.0.dev2/mousetools/urls.py +0 -4
- mousetools-3.1.0.dev2/tests/test_auth.py +0 -8
- mousetools-3.1.0.dev2/tests/test_entities.py +0 -239
- mousetools-3.1.0.dev2/tests/test_ids.py +0 -103
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/MouseTools.egg-info/dependency_links.txt +0 -0
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/MouseTools.egg-info/top_level.txt +0 -0
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/README.md +0 -0
- {mousetools-3.1.0.dev2/mousetools/mixins → mousetools-4.0.0b0/mousetools/api}/__init__.py +0 -0
- {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/setup.cfg +0 -0
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: MouseTools
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0b0
|
|
4
4
|
Summary: An unofficial Python wrapper for Disney's API
|
|
5
5
|
Author: Scott Caratozzolo
|
|
6
|
+
License-Expression: MIT
|
|
6
7
|
Project-URL: repository, https://gitlab.com/caratozzoloxyz/public/MouseTools
|
|
7
8
|
Project-URL: documentation, https://caratozzoloxyz.gitlab.io/public/MouseTools/
|
|
8
9
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Requires-Python: >=3.9
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: httpx>=0.27.2
|
|
15
|
+
Requires-Dist: pycryptodome>=3.22.0
|
|
15
16
|
Requires-Dist: tzdata>=2024.1
|
|
17
|
+
Dynamic: license-file
|
|
16
18
|
|
|
17
19
|
# MouseTools
|
|
18
20
|
[](https://badge.fury.io/py/MouseTools) [](https://pepy.tech/project/mousetools)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
MouseTools.egg-info/PKG-INFO
|
|
5
|
+
MouseTools.egg-info/SOURCES.txt
|
|
6
|
+
MouseTools.egg-info/dependency_links.txt
|
|
7
|
+
MouseTools.egg-info/requires.txt
|
|
8
|
+
MouseTools.egg-info/top_level.txt
|
|
9
|
+
mousetools/__init__.py
|
|
10
|
+
mousetools/auth.py
|
|
11
|
+
mousetools/api/__init__.py
|
|
12
|
+
mousetools/api/menu.py
|
|
13
|
+
mousetools/channels/__init__.py
|
|
14
|
+
mousetools/channels/enums.py
|
|
15
|
+
mousetools/channels/facilities/__init__.py
|
|
16
|
+
mousetools/channels/facilities/enums.py
|
|
17
|
+
mousetools/channels/facilitystatus/__init__.py
|
|
18
|
+
mousetools/mixins/__init__.py
|
|
19
|
+
mousetools/mixins/couchbase.py
|
|
20
|
+
mousetools/mixins/disney.py
|
|
21
|
+
mousetools/resources/environments.bin
|
|
22
|
+
tests/test_mixins.py
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: MouseTools
|
|
3
|
-
Version:
|
|
3
|
+
Version: 4.0.0b0
|
|
4
4
|
Summary: An unofficial Python wrapper for Disney's API
|
|
5
5
|
Author: Scott Caratozzolo
|
|
6
|
+
License-Expression: MIT
|
|
6
7
|
Project-URL: repository, https://gitlab.com/caratozzoloxyz/public/MouseTools
|
|
7
8
|
Project-URL: documentation, https://caratozzoloxyz.gitlab.io/public/MouseTools/
|
|
8
9
|
Classifier: Programming Language :: Python :: 3
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Requires-Python: >=3.9
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
13
|
License-File: LICENSE
|
|
14
14
|
Requires-Dist: httpx>=0.27.2
|
|
15
|
+
Requires-Dist: pycryptodome>=3.22.0
|
|
15
16
|
Requires-Dist: tzdata>=2024.1
|
|
17
|
+
Dynamic: license-file
|
|
16
18
|
|
|
17
19
|
# MouseTools
|
|
18
20
|
[](https://badge.fury.io/py/MouseTools) [](https://pepy.tech/project/mousetools)
|
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from mousetools.auth import auth_obj
|
|
4
|
+
from mousetools.mixins.disney import DisneyAPIMixin
|
|
5
|
+
|
|
6
|
+
# menus -> menu -> menugroups -> menuitems
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class MenuItem:
|
|
10
|
+
def __init__(self, raw_item_data: dict, parent_resturant_id: typing.Optional[str] = None):
|
|
11
|
+
self._raw_item_data = raw_item_data
|
|
12
|
+
self._parent_restaurant_id = parent_resturant_id
|
|
13
|
+
|
|
14
|
+
def __repr__(self) -> str:
|
|
15
|
+
return f"MenuItem(entity_id={self.entity_id}, parent_resturant_id={self._parent_restaurant_id})"
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def entity_id(self) -> typing.Optional[str]:
|
|
19
|
+
"""
|
|
20
|
+
The entity id of the menu item.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
(typing.Optional[str]): The entity id of the menu item.
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
return self._raw_item_data["id"]
|
|
27
|
+
except KeyError:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
@property
|
|
31
|
+
def pc_short_name(self) -> typing.Optional[str]:
|
|
32
|
+
"""
|
|
33
|
+
The short name of the menu item.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
(typing.Optional[str]): The short name of the menu item.
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
return self._raw_item_data["names"]["PCShort"]
|
|
40
|
+
except KeyError:
|
|
41
|
+
return None
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def pc_long_name(self) -> typing.Optional[str]:
|
|
45
|
+
"""
|
|
46
|
+
The long name of the menu item.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
(typing.Optional[str]): The long name of the menu item.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
return self._raw_item_data["names"]["PCLong"]
|
|
53
|
+
except KeyError:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def mobile_short_name(self) -> typing.Optional[str]:
|
|
58
|
+
"""
|
|
59
|
+
The short name of the menu item.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
(typing.Optional[str]): The short name of the menu item.
|
|
63
|
+
"""
|
|
64
|
+
try:
|
|
65
|
+
return self._raw_item_data["names"]["MobileShort"]
|
|
66
|
+
except KeyError:
|
|
67
|
+
return None
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def mobile_long_name(self) -> typing.Optional[str]:
|
|
71
|
+
"""
|
|
72
|
+
The long name of the menu item.
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
(typing.Optional[str]): The long name of the menu item.
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
return self._raw_item_data["names"]["MobileLong"]
|
|
79
|
+
except KeyError:
|
|
80
|
+
return None
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def mickey_check(self) -> typing.Optional[bool]:
|
|
84
|
+
"""
|
|
85
|
+
Whether the menu item is mickey check.
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
(typing.Optional[bool]): Whether the menu item is mickey check.
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
return self._raw_item_data["mickeyCheck"]
|
|
92
|
+
except KeyError:
|
|
93
|
+
return None
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def pc_long_description(self) -> typing.Optional[str]:
|
|
97
|
+
"""
|
|
98
|
+
The long description of the menu item.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
(typing.Optional[str]): The long description of the menu item.
|
|
102
|
+
"""
|
|
103
|
+
try:
|
|
104
|
+
return self._raw_item_data["descriptions"]["PCLong"]["text"]
|
|
105
|
+
except KeyError:
|
|
106
|
+
return None
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def mobile_short_description(self) -> typing.Optional[str]:
|
|
110
|
+
"""
|
|
111
|
+
The short description of the menu item.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
(typing.Optional[str]): The short description of the menu item.
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
return self._raw_item_data["descriptions"]["MobileShort"]["text"]
|
|
118
|
+
except KeyError:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
@property
|
|
122
|
+
def default_selection(self) -> typing.Optional[bool]:
|
|
123
|
+
"""
|
|
124
|
+
Whether the menu item is the default selection.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
(typing.Optional[bool]): Whether the menu item is the default selection.
|
|
128
|
+
"""
|
|
129
|
+
try:
|
|
130
|
+
return self._raw_item_data["defaultSelection"]
|
|
131
|
+
except KeyError:
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def prices(self) -> typing.Optional[dict]:
|
|
136
|
+
"""
|
|
137
|
+
The prices of the menu item.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
(typing.Optional[dict]): The prices of the menu item.
|
|
141
|
+
"""
|
|
142
|
+
try:
|
|
143
|
+
return self._raw_item_data["prices"]
|
|
144
|
+
except KeyError:
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def per_serving_without_tax(self) -> typing.Optional[float]:
|
|
149
|
+
"""
|
|
150
|
+
The per serving without tax of the menu item.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
(typing.Optional[float]): The per serving without tax of the menu item.
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
return self._raw_item_data["prices"]["PerServing"]["withoutTax"]
|
|
157
|
+
except KeyError:
|
|
158
|
+
return None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class MenuGroup:
|
|
162
|
+
def __init__(self, raw_menu_group_data: dict, parent_restaurant_id: typing.Optional[str] = None):
|
|
163
|
+
self._raw_menu_group_data = raw_menu_group_data
|
|
164
|
+
self._parent_restaurant_id = parent_restaurant_id
|
|
165
|
+
|
|
166
|
+
def __str__(self) -> str:
|
|
167
|
+
return f"MenuGroup(menu_group_type={self.menu_group_type}, parent_restaurant_id={self._parent_restaurant_id})"
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def menu_group_type(self) -> typing.Optional[str]:
|
|
171
|
+
"""
|
|
172
|
+
The type of the menu group.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
(typing.Optional[str]): The type of the menu group.
|
|
176
|
+
"""
|
|
177
|
+
try:
|
|
178
|
+
return self._raw_menu_group_data["menuGroupType"]
|
|
179
|
+
except KeyError:
|
|
180
|
+
return None
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def multiple_price_types(self) -> typing.Optional[bool]:
|
|
184
|
+
"""
|
|
185
|
+
Whether the menu group has multiple price types.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
(typing.Optional[bool]): Whether the menu group has multiple price types.
|
|
189
|
+
"""
|
|
190
|
+
try:
|
|
191
|
+
return self._raw_menu_group_data["multiplePriceTypes"]
|
|
192
|
+
except KeyError:
|
|
193
|
+
return None
|
|
194
|
+
|
|
195
|
+
@property
|
|
196
|
+
def mickey_check_items(self) -> typing.Optional[bool]:
|
|
197
|
+
"""
|
|
198
|
+
Whether the menu group has mickey check items.
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
(typing.Optional[bool]): Whether the menu group has mickey check items.
|
|
202
|
+
"""
|
|
203
|
+
try:
|
|
204
|
+
return self._raw_menu_group_data["mickeyCheckItems"]
|
|
205
|
+
except KeyError:
|
|
206
|
+
return None
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def names(self) -> typing.Optional[dict]:
|
|
210
|
+
"""
|
|
211
|
+
The names of the menu group.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
(typing.Optional[dict]): The names of the menu group.
|
|
215
|
+
"""
|
|
216
|
+
try:
|
|
217
|
+
return self._raw_menu_group_data["names"]
|
|
218
|
+
except KeyError:
|
|
219
|
+
return None
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def pc_short_name(self) -> typing.Optional[str]:
|
|
223
|
+
"""
|
|
224
|
+
The short name of the menu group.
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
(typing.Optional[str]): The short name of the menu group.
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
return self._raw_menu_group_data["names"]["PCShort"]
|
|
231
|
+
except KeyError:
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def pc_long_name(self) -> typing.Optional[str]:
|
|
236
|
+
"""
|
|
237
|
+
The long name of the menu group.
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
(typing.Optional[str]): The long name of the menu group.
|
|
241
|
+
"""
|
|
242
|
+
try:
|
|
243
|
+
return self._raw_menu_group_data["names"]["PCLong"]
|
|
244
|
+
except KeyError:
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def mobile_short_name(self) -> typing.Optional[str]:
|
|
249
|
+
"""
|
|
250
|
+
The short name of the menu group.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
(typing.Optional[str]): The short name of the menu group.
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
return self._raw_menu_group_data["names"]["MobileShort"]
|
|
257
|
+
except KeyError:
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
@property
|
|
261
|
+
def mobile_long_name(self) -> typing.Optional[str]:
|
|
262
|
+
"""
|
|
263
|
+
The long name of the menu group.
|
|
264
|
+
|
|
265
|
+
Returns:
|
|
266
|
+
(typing.Optional[str]): The long name of the menu group.
|
|
267
|
+
"""
|
|
268
|
+
try:
|
|
269
|
+
return self._raw_menu_group_data["names"]["MobileLong"]
|
|
270
|
+
except KeyError:
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
@property
|
|
274
|
+
def menu_items(self) -> typing.Optional[list[MenuItem]]:
|
|
275
|
+
"""
|
|
276
|
+
The menu items of the menu group.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
(typing.Optional[list[MenuItem]]): The menu items of the menu group.
|
|
280
|
+
"""
|
|
281
|
+
try:
|
|
282
|
+
return [MenuItem(i, self._parent_restaurant_id) for i in self._raw_menu_group_data["menuItems"]]
|
|
283
|
+
except KeyError:
|
|
284
|
+
return None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
class Menu:
|
|
288
|
+
def __init__(self, raw_menu_data: dict, parent_restaurant_id: typing.Optional[str] = None):
|
|
289
|
+
self._raw_menu_data = raw_menu_data
|
|
290
|
+
self._parent_restaurant_id = parent_restaurant_id
|
|
291
|
+
|
|
292
|
+
def __str__(self):
|
|
293
|
+
return f"Menu(entity_id={self.entity_id}, menu_type={self.menu_type}, parent_restaurant_id={self._parent_restaurant_id})"
|
|
294
|
+
|
|
295
|
+
@property
|
|
296
|
+
def entity_id(self) -> typing.Optional[str]:
|
|
297
|
+
"""
|
|
298
|
+
The entity id of the menu.
|
|
299
|
+
|
|
300
|
+
Returns:
|
|
301
|
+
(typing.Optional[str]): The entity id of the menu.
|
|
302
|
+
"""
|
|
303
|
+
try:
|
|
304
|
+
return self._raw_menu_data["id"]
|
|
305
|
+
except KeyError:
|
|
306
|
+
return None
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def menu_type(self) -> typing.Optional[str]:
|
|
310
|
+
"""
|
|
311
|
+
The type of the menu.
|
|
312
|
+
|
|
313
|
+
Returns:
|
|
314
|
+
(typing.Optional[str]): The type of the menu.
|
|
315
|
+
"""
|
|
316
|
+
try:
|
|
317
|
+
return self._raw_menu_data["menuType"]
|
|
318
|
+
except KeyError:
|
|
319
|
+
return None
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def localized_menu_type(self) -> typing.Optional[str]:
|
|
323
|
+
"""
|
|
324
|
+
The localized type of the menu.
|
|
325
|
+
|
|
326
|
+
Returns:
|
|
327
|
+
(typing.Optional[str]): The localized type of the menu.
|
|
328
|
+
"""
|
|
329
|
+
try:
|
|
330
|
+
return self._raw_menu_data["localizedMenuType"]
|
|
331
|
+
except KeyError:
|
|
332
|
+
return None
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def experience_type(self) -> typing.Optional[str]:
|
|
336
|
+
"""
|
|
337
|
+
The experience type of the menu.
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
(typing.Optional[str]): The experience type of the menu.
|
|
341
|
+
"""
|
|
342
|
+
try:
|
|
343
|
+
return self._raw_menu_data["experienceType"]
|
|
344
|
+
except KeyError:
|
|
345
|
+
return None
|
|
346
|
+
|
|
347
|
+
@property
|
|
348
|
+
def service_style(self) -> typing.Optional[str]:
|
|
349
|
+
"""
|
|
350
|
+
The service style of the menu.
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
(typing.Optional[str]): The service style of the menu.
|
|
354
|
+
"""
|
|
355
|
+
try:
|
|
356
|
+
return self._raw_menu_data["serviceStyle"]
|
|
357
|
+
except KeyError:
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
@property
|
|
361
|
+
def primary_cuisine_type(self) -> typing.Optional[str]:
|
|
362
|
+
"""
|
|
363
|
+
The primary cuisine type of the menu.
|
|
364
|
+
|
|
365
|
+
Returns:
|
|
366
|
+
(typing.Optional[str]): The primary cuisine type of the menu.
|
|
367
|
+
"""
|
|
368
|
+
try:
|
|
369
|
+
return self._raw_menu_data["primaryCuisineType"]
|
|
370
|
+
except KeyError:
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
@property
|
|
374
|
+
def secondary_cuisine_type(self) -> typing.Optional[str]:
|
|
375
|
+
"""
|
|
376
|
+
The secondary cuisine type of the menu.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
(typing.Optional[str]): The secondary cuisine type of the menu.
|
|
380
|
+
"""
|
|
381
|
+
try:
|
|
382
|
+
return self._raw_menu_data["secondaryCuisineType"]
|
|
383
|
+
except KeyError:
|
|
384
|
+
return None
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def menu_groups(self) -> list[MenuGroup]:
|
|
388
|
+
"""
|
|
389
|
+
The menu groups of the menu.
|
|
390
|
+
|
|
391
|
+
Returns:
|
|
392
|
+
(list[MenuGroup]): The menu items of the menu.
|
|
393
|
+
"""
|
|
394
|
+
try:
|
|
395
|
+
return [MenuGroup(i, self._parent_restaurant_id) for i in self._raw_menu_data["menuGroups"]]
|
|
396
|
+
except KeyError:
|
|
397
|
+
return []
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
class Menus(DisneyAPIMixin):
|
|
401
|
+
"""Class for Menu Entities."""
|
|
402
|
+
|
|
403
|
+
_menu_service_base = f"{auth_obj._environments['serviceMenuUrl']}/diningMenuSvc/orchestration/menus"
|
|
404
|
+
|
|
405
|
+
def __init__(self, restaurant_channel_id: str, lazy_load: bool = True):
|
|
406
|
+
self.entity_id = restaurant_channel_id.rsplit(".", 1)[-1]
|
|
407
|
+
self._menu_service_url = f"{self._menu_service_base}/{self.entity_id}"
|
|
408
|
+
|
|
409
|
+
if lazy_load:
|
|
410
|
+
self.disney_data: typing.Optional[dict] = None
|
|
411
|
+
else:
|
|
412
|
+
self.disney_data = self.get_disney_data(self.entity_id)
|
|
413
|
+
|
|
414
|
+
def __str__(self):
|
|
415
|
+
return f"Menus(entity_id={self.entity_id})"
|
|
416
|
+
|
|
417
|
+
def __repr__(self):
|
|
418
|
+
return f"Menus(restaurant_channel_id={self.entity_id})"
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def menus(self) -> list[Menu]:
|
|
422
|
+
"""
|
|
423
|
+
Returns a list of all the menus associated with this entity.
|
|
424
|
+
|
|
425
|
+
Returns:
|
|
426
|
+
(list[Menu]): List of Menu objects that are associated with this entity.
|
|
427
|
+
"""
|
|
428
|
+
if self.disney_data is None:
|
|
429
|
+
self.disney_data = self.get_disney_data(self._menu_service_url)
|
|
430
|
+
try:
|
|
431
|
+
return [Menu(i, self.entity_id) for i in self.disney_data["menus"]]
|
|
432
|
+
except KeyError:
|
|
433
|
+
return []
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import json
|
|
3
|
+
import typing
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from importlib.resources import as_file, files
|
|
6
|
+
|
|
7
|
+
import httpx
|
|
8
|
+
from Crypto.Cipher import AES
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Authentication:
|
|
12
|
+
def __init__(self):
|
|
13
|
+
self.time_of_expire: typing.Optional[datetime] = None
|
|
14
|
+
self.access_token: typing.Optional[str] = None
|
|
15
|
+
self._environments = self._decrypt_file("resources/environments.bin").get("production", {})
|
|
16
|
+
|
|
17
|
+
def authenticate(self) -> tuple[str, int]:
|
|
18
|
+
"""Gets an access token from Disney and the token's expiration time
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
(tuple[str, int]): Access token to the api and how long until it expires
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
headers = {
|
|
25
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36",
|
|
26
|
+
"Accept-Language": "en-US,en;q=0.9",
|
|
27
|
+
"Cache-Control": "max-age=0",
|
|
28
|
+
"Accept": "application/json",
|
|
29
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
30
|
+
"Connection": "keep-alive",
|
|
31
|
+
"Proxy-Connection": "keep-alive",
|
|
32
|
+
"Accept-Encoding": "gzip, deflate",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
body = {
|
|
36
|
+
"grant_type": "assertion",
|
|
37
|
+
"assertion_type": "public",
|
|
38
|
+
"client_id": self._environments["authzClientId"],
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
response = httpx.post(
|
|
42
|
+
f"{self._environments['authzServiceUrl']}/token",
|
|
43
|
+
headers=headers,
|
|
44
|
+
params=body,
|
|
45
|
+
timeout=10,
|
|
46
|
+
)
|
|
47
|
+
response.raise_for_status()
|
|
48
|
+
auth = response.json()
|
|
49
|
+
|
|
50
|
+
return auth["access_token"], int(auth["expires_in"])
|
|
51
|
+
|
|
52
|
+
def get_headers(self) -> dict[str, str]:
|
|
53
|
+
"""Creates the headers to send during the request
|
|
54
|
+
|
|
55
|
+
Returns:
|
|
56
|
+
(dict[str, str]): Headers for the request
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
if self.time_of_expire is None or (datetime.now() > self.time_of_expire):
|
|
60
|
+
access_token, expires_in = self.authenticate()
|
|
61
|
+
self.time_of_expire = datetime.now() + timedelta(seconds=(expires_in - 10))
|
|
62
|
+
self.access_token = access_token
|
|
63
|
+
|
|
64
|
+
headers = {
|
|
65
|
+
"Authorization": f"BEARER {self.access_token}",
|
|
66
|
+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
|
|
67
|
+
"Content-Type": "application/json",
|
|
68
|
+
"Accept": "*/*",
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return headers
|
|
72
|
+
|
|
73
|
+
def get_couchbase_headers(self) -> dict[str, str]:
|
|
74
|
+
"""Creates the headers to send during the couchbase request
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
(dict[str, str]): Headers for the couchbase request
|
|
78
|
+
"""
|
|
79
|
+
username = self._environments["syncGatewayUser"]
|
|
80
|
+
password = self._environments["syncGatewayPass"]
|
|
81
|
+
token = base64.b64encode(f"{username}:{password}".encode()).decode()
|
|
82
|
+
|
|
83
|
+
header = {
|
|
84
|
+
"Authorization": f"Basic {token}",
|
|
85
|
+
"User-Agent": "CouchbaseLite/3.2.1-9 (Java; Android 16; sdk_gphone64_x86_64) EE/release, Commit/2109502be2@02fbcb1b8b44 Core/3.2.1 (19)",
|
|
86
|
+
"Content-Type": "application/json",
|
|
87
|
+
"Accept": "multipart/related",
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return header
|
|
91
|
+
|
|
92
|
+
def _decrypt_file(self, filename: str) -> dict:
|
|
93
|
+
cipher = AES.new(self._e(), AES.MODE_GCM, nonce=self._b())
|
|
94
|
+
with as_file(files("mousetools") / filename) as f, open(f, "rb") as file:
|
|
95
|
+
decrypted = cipher.decrypt(file.read())
|
|
96
|
+
return json.loads(decrypted.decode("Windows-1252")[:-16])
|
|
97
|
+
|
|
98
|
+
def _e(self):
|
|
99
|
+
s = ""
|
|
100
|
+
for i in range(1, 17):
|
|
101
|
+
if i % 2 != 0 or i >= 16:
|
|
102
|
+
s += chr(i + 65)
|
|
103
|
+
else:
|
|
104
|
+
s += str(3.141592653589793)[i]
|
|
105
|
+
bytes_ = s.encode("utf-8")
|
|
106
|
+
return bytes_
|
|
107
|
+
|
|
108
|
+
def _b(self):
|
|
109
|
+
return bytes(reversed(self._e()))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
auth_obj = Authentication()
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
from mousetools.channels.enums import DestinationShort, DestinationTimezones, DLRCouchbaseChannels, WDWCouchbaseChannels
|
|
6
|
+
from mousetools.mixins.couchbase import CouchbaseMixin
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class CouchbaseChannel(CouchbaseMixin):
|
|
10
|
+
def __init__(self, channel_id: typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels], lazy_load: bool = True):
|
|
11
|
+
"""
|
|
12
|
+
Args:
|
|
13
|
+
channel_id (typing.Union[WDWCouchbaseChannels, DLRCouchbaseChannels]): Channel ID from the enum
|
|
14
|
+
lazy_load (bool, optional): If True, will not pull data until a method or property is called. Defaults to True.
|
|
15
|
+
"""
|
|
16
|
+
if isinstance(channel_id, Enum):
|
|
17
|
+
channel_id = channel_id.value
|
|
18
|
+
self.channel_id = channel_id
|
|
19
|
+
|
|
20
|
+
self._destination_short: DestinationShort = channel_id.split(".")[0]
|
|
21
|
+
self._tz: DestinationTimezones = (
|
|
22
|
+
DestinationTimezones.WALT_DISNEY_WORLD
|
|
23
|
+
if self._destination_short == DestinationShort.WALT_DISNEY_WORLD
|
|
24
|
+
else DestinationTimezones.DISNEYLAND_RESORT
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
self._cb_data = None
|
|
28
|
+
self._refresh_interval = timedelta(minutes=10)
|
|
29
|
+
self.last_update = datetime.now(tz=self._tz.value)
|
|
30
|
+
|
|
31
|
+
if not lazy_load:
|
|
32
|
+
self.refresh()
|
|
33
|
+
|
|
34
|
+
def refresh(self) -> None:
|
|
35
|
+
"""Pulls initial data if none exists or if it is older than the refresh interval"""
|
|
36
|
+
if self._cb_data is None or datetime.now(tz=self._tz.value) - self.last_update > self._refresh_interval:
|
|
37
|
+
self._cb_data = self.get_channel_changes(self.channel_id)
|
|
38
|
+
|
|
39
|
+
def get_children_channel_ids(self) -> list[str]:
|
|
40
|
+
"""Returns a list of child channel ids for the channel."""
|
|
41
|
+
self.refresh()
|
|
42
|
+
|
|
43
|
+
return [i["id"] for i in self._cb_data["results"]]
|
|
44
|
+
|
|
45
|
+
def __repr__(self):
|
|
46
|
+
return f"{self.__class__.__name__}({self.channel_id})"
|