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.
Files changed (53) hide show
  1. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/LICENSE +1 -1
  2. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0/MouseTools.egg-info}/PKG-INFO +5 -3
  3. mousetools-4.0.0b0/MouseTools.egg-info/SOURCES.txt +22 -0
  4. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/MouseTools.egg-info/requires.txt +1 -0
  5. {mousetools-3.1.0.dev2/MouseTools.egg-info → mousetools-4.0.0b0}/PKG-INFO +5 -3
  6. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/mousetools/__init__.py +1 -1
  7. mousetools-4.0.0b0/mousetools/api/menu.py +433 -0
  8. mousetools-4.0.0b0/mousetools/auth.py +112 -0
  9. mousetools-4.0.0b0/mousetools/channels/__init__.py +46 -0
  10. mousetools-4.0.0b0/mousetools/channels/enums.py +67 -0
  11. mousetools-4.0.0b0/mousetools/channels/facilities/__init__.py +520 -0
  12. mousetools-4.0.0b0/mousetools/channels/facilities/enums.py +30 -0
  13. mousetools-4.0.0b0/mousetools/channels/facilitystatus/__init__.py +153 -0
  14. mousetools-4.0.0b0/mousetools/mixins/__init__.py +0 -0
  15. mousetools-4.0.0b0/mousetools/mixins/couchbase.py +80 -0
  16. mousetools-4.0.0b0/mousetools/mixins/disney.py +36 -0
  17. mousetools-4.0.0b0/mousetools/resources/environments.bin +0 -0
  18. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/pyproject.toml +19 -45
  19. mousetools-4.0.0b0/tests/test_mixins.py +24 -0
  20. mousetools-3.1.0.dev2/MouseTools.egg-info/SOURCES.txt +0 -38
  21. mousetools-3.1.0.dev2/mousetools/auth.py +0 -70
  22. mousetools-3.1.0.dev2/mousetools/entities/__init__.py +0 -43
  23. mousetools-3.1.0.dev2/mousetools/entities/attraction.py +0 -12
  24. mousetools-3.1.0.dev2/mousetools/entities/base.py +0 -488
  25. mousetools-3.1.0.dev2/mousetools/entities/character.py +0 -12
  26. mousetools-3.1.0.dev2/mousetools/entities/destination.py +0 -16
  27. mousetools-3.1.0.dev2/mousetools/entities/dining_event.py +0 -12
  28. mousetools-3.1.0.dev2/mousetools/entities/dinner_show.py +0 -12
  29. mousetools-3.1.0.dev2/mousetools/entities/entertainment.py +0 -12
  30. mousetools-3.1.0.dev2/mousetools/entities/entertainment_venue.py +0 -12
  31. mousetools-3.1.0.dev2/mousetools/entities/event.py +0 -12
  32. mousetools-3.1.0.dev2/mousetools/entities/guest_services.py +0 -12
  33. mousetools-3.1.0.dev2/mousetools/entities/land.py +0 -12
  34. mousetools-3.1.0.dev2/mousetools/entities/menu.py +0 -226
  35. mousetools-3.1.0.dev2/mousetools/entities/merchandise_facility.py +0 -12
  36. mousetools-3.1.0.dev2/mousetools/entities/park.py +0 -42
  37. mousetools-3.1.0.dev2/mousetools/entities/point_of_interest.py +0 -12
  38. mousetools-3.1.0.dev2/mousetools/entities/resort.py +0 -12
  39. mousetools-3.1.0.dev2/mousetools/entities/resort_area.py +0 -12
  40. mousetools-3.1.0.dev2/mousetools/entities/restaurant.py +0 -53
  41. mousetools-3.1.0.dev2/mousetools/exceptions.py +0 -17
  42. mousetools-3.1.0.dev2/mousetools/ids.py +0 -96
  43. mousetools-3.1.0.dev2/mousetools/mixins/disney.py +0 -55
  44. mousetools-3.1.0.dev2/mousetools/mixins/themeparksapi.py +0 -128
  45. mousetools-3.1.0.dev2/mousetools/urls.py +0 -4
  46. mousetools-3.1.0.dev2/tests/test_auth.py +0 -8
  47. mousetools-3.1.0.dev2/tests/test_entities.py +0 -239
  48. mousetools-3.1.0.dev2/tests/test_ids.py +0 -103
  49. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/MouseTools.egg-info/dependency_links.txt +0 -0
  50. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/MouseTools.egg-info/top_level.txt +0 -0
  51. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/README.md +0 -0
  52. {mousetools-3.1.0.dev2/mousetools/mixins → mousetools-4.0.0b0/mousetools/api}/__init__.py +0 -0
  53. {mousetools-3.1.0.dev2 → mousetools-4.0.0b0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Scott Caratozzolo
3
+ Copyright (c) 2025 Scott Caratozzolo
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,18 +1,20 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: MouseTools
3
- Version: 3.1.0.dev2
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
  [![PyPI version](https://badge.fury.io/py/MouseTools.svg)](https://badge.fury.io/py/MouseTools) [![Downloads](https://pepy.tech/badge/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,2 +1,3 @@
1
1
  httpx>=0.27.2
2
+ pycryptodome>=3.22.0
2
3
  tzdata>=2024.1
@@ -1,18 +1,20 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: MouseTools
3
- Version: 3.1.0.dev2
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
  [![PyPI version](https://badge.fury.io/py/MouseTools.svg)](https://badge.fury.io/py/MouseTools) [![Downloads](https://pepy.tech/badge/mousetools)](https://pepy.tech/project/mousetools)
@@ -3,5 +3,5 @@ import logging
3
3
  logger = logging.getLogger(__name__)
4
4
  logger.addHandler(logging.NullHandler())
5
5
 
6
- __version__ = "3.1.0-dev.2" # will be set at runtime
6
+ __version__ = "0.0.0" # will be set at runtime
7
7
  logger.info(f"MouseTools version: {__version__}")
@@ -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})"