ambr-py 1.7.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ambr/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .client import *
2
+ from .exceptions import *
3
+ from .models import *
ambr/client.py ADDED
@@ -0,0 +1,657 @@
1
+ import logging
2
+ from enum import Enum
3
+ from typing import Any, Final
4
+
5
+ from aiohttp_client_cache.backends.sqlite import SQLiteBackend
6
+ from aiohttp_client_cache.session import CachedSession
7
+
8
+ from .exceptions import AmbrAPIError, ConnectionTimeoutError, DataNotFoundError
9
+ from .models import (
10
+ AbyssResponse,
11
+ AchievementCategory,
12
+ ArtifactSet,
13
+ ArtifactSetDetail,
14
+ Book,
15
+ BookDetail,
16
+ Changelog,
17
+ Character,
18
+ CharacterDetail,
19
+ CharacterFetter,
20
+ Domains,
21
+ Food,
22
+ FoodDetail,
23
+ Furniture,
24
+ FurnitureDetail,
25
+ Material,
26
+ MaterialDetail,
27
+ Monster,
28
+ MonsterDetail,
29
+ Namecard,
30
+ NamecardDetail,
31
+ Quest,
32
+ TCGCard,
33
+ TCGCardDetail,
34
+ UpgradeData,
35
+ Weapon,
36
+ WeaponDetail,
37
+ )
38
+ from .models.furniture import FurnitureSet, FurnitureSetDetail
39
+ from .utils import remove_html_tags
40
+
41
+ __all__ = ("AmbrAPI", "Language")
42
+
43
+
44
+ class Language(Enum):
45
+ CHT = "cht"
46
+ CHS = "chs"
47
+ DE = "de"
48
+ EN = "en"
49
+ ES = "es"
50
+ FR = "fr"
51
+ ID = "id"
52
+ JP = "jp"
53
+ KR = "kr"
54
+ PT = "pt"
55
+ RU = "ru"
56
+ TH = "th"
57
+ VI = "vi"
58
+ IT = "it"
59
+ TR = "tr"
60
+
61
+
62
+ LOGGER_ = logging.getLogger("ambr.py")
63
+
64
+
65
+ class AmbrAPI:
66
+ BASE_URL: Final[str] = "https://api.ambr.top/v2"
67
+
68
+ def __init__(
69
+ self,
70
+ *,
71
+ lang: Language = Language.EN,
72
+ cache_ttl: int = 3600,
73
+ headers: dict[str, Any] | None = None,
74
+ ) -> None:
75
+ self.lang = lang
76
+ self.cache_ttl = cache_ttl
77
+
78
+ self._session: CachedSession | None = None
79
+ self._cache = SQLiteBackend("./.cache/ambr/aiohttp-cache.db", expire_after=cache_ttl)
80
+ self._headers = headers or {"User-Agent": "ambr-py"}
81
+
82
+ async def __aenter__(self) -> "AmbrAPI":
83
+ await self.start()
84
+ return self
85
+
86
+ async def __aexit__(self, exc_type, exc, tb) -> None: # noqa: ANN001
87
+ await self.close()
88
+
89
+ async def _request(
90
+ self, endpoint: str, *, static: bool = False, use_cache: bool
91
+ ) -> dict[str, Any]:
92
+ """
93
+ A helper function to make requests to the API.
94
+
95
+ Parameters
96
+ ----------
97
+ endpoint: :class:`str`
98
+ The endpoint to request from.
99
+ static: :class:`bool`
100
+ Whether to use the static endpoint or not. Defaults to ``False``.
101
+ use_cache: :class:`bool`
102
+ Whether to use the cache or not. Defaults to ``True``.
103
+
104
+ Returns
105
+ -------
106
+ Dict[str, Any]
107
+ The response from the API.
108
+ """
109
+ if self._session is None:
110
+ msg = "Call `start` before making requests."
111
+ raise RuntimeError(msg)
112
+
113
+ if static:
114
+ url = f"{self.BASE_URL}/static/{endpoint}"
115
+ else:
116
+ url = f"{self.BASE_URL}/{self.lang.value}/{endpoint}"
117
+
118
+ LOGGER_.debug("Requesting %s...", url)
119
+
120
+ if not use_cache:
121
+ async with self._session.disabled(), self._session.get(url) as resp:
122
+ if resp.status != 200:
123
+ self._handle_error(resp.status)
124
+ data = await resp.json()
125
+ else:
126
+ async with self._session.get(url) as resp:
127
+ if resp.status != 200:
128
+ self._handle_error(resp.status)
129
+ data = await resp.json()
130
+
131
+ return data
132
+
133
+ def _handle_error(self, code: int) -> None:
134
+ """
135
+ A helper function to handle errors.
136
+ """
137
+ match code:
138
+ case 404:
139
+ raise DataNotFoundError
140
+ case 522 | 524:
141
+ raise ConnectionTimeoutError
142
+ case _:
143
+ raise AmbrAPIError(code)
144
+
145
+ async def start(self) -> None:
146
+ """
147
+ Starts the client session.
148
+ """
149
+ self._session = CachedSession(headers=self._headers, cache=self._cache)
150
+
151
+ async def close(self) -> None:
152
+ """
153
+ Closes the client session.
154
+ """
155
+ if self._session is not None:
156
+ await self._session.close()
157
+
158
+ async def fetch_achievement_categories(
159
+ self, use_cache: bool = True
160
+ ) -> list[AchievementCategory]:
161
+ """
162
+ Fetches all achievement categories.
163
+
164
+ Returns
165
+ -------
166
+ List[:class:`AchievementCategory`]
167
+ The achievement categories.
168
+ """
169
+ data = await self._request("achievement", use_cache=use_cache)
170
+ return [
171
+ AchievementCategory(**achievement_category)
172
+ for achievement_category in data["data"].values()
173
+ ]
174
+
175
+ async def fetch_artifact_sets(self, use_cache: bool = True) -> list[ArtifactSet]:
176
+ """
177
+ Fetches all artifact sets.
178
+
179
+ Returns
180
+ -------
181
+ List[:class:`ArtifactSet`]
182
+ The artifact sets.
183
+ """
184
+ data = await self._request("reliquary", use_cache=use_cache)
185
+ return [ArtifactSet(**artifact_set) for artifact_set in data["data"]["items"].values()]
186
+
187
+ async def fetch_artifact_set_detail(self, id: int, use_cache: bool = True) -> ArtifactSetDetail:
188
+ """
189
+ Fetches an artifact set detail by ID.
190
+
191
+ Parameters
192
+ ----------
193
+ id: :class:`int`
194
+ The ID of the artifact set detail to fetch.
195
+
196
+ Returns
197
+ -------
198
+ :class:`ArtifactSetDetail`
199
+ The artifact set detail.
200
+ """
201
+ data = await self._request(f"reliquary/{id}", use_cache=use_cache)
202
+ return ArtifactSetDetail(**data["data"])
203
+
204
+ async def fetch_books(self, use_cache: bool = True) -> list[Book]:
205
+ """
206
+ Fetches all books.
207
+
208
+ Returns
209
+ -------
210
+ List[:class:`Book`]
211
+ The books.
212
+ """
213
+ data = await self._request("book", use_cache=use_cache)
214
+ return [Book(**book) for book in data["data"]["items"].values()]
215
+
216
+ async def fetch_book_detail(self, id: int, use_cache: bool = True) -> BookDetail:
217
+ """
218
+ Fetches a book detail by ID.
219
+
220
+ Parameters
221
+ ----------
222
+ id: :class:`int`
223
+ The ID of the book detail to fetch.
224
+
225
+ Returns
226
+ -------
227
+ :class:`BookDetail`
228
+ The book detail.
229
+ """
230
+ data = await self._request(f"book/{id}", use_cache=use_cache)
231
+ return BookDetail(**data["data"])
232
+
233
+ async def fetch_characters(self, use_cache: bool = True) -> list[Character]:
234
+ """
235
+ Fetches all characters.
236
+
237
+ Returns
238
+ -------
239
+ List[:class:`Character`]
240
+ The characters.
241
+ """
242
+ data = await self._request("avatar", use_cache=use_cache)
243
+ return [Character(**character) for character in data["data"]["items"].values()]
244
+
245
+ async def fetch_character_detail(self, id: str, use_cache: bool = True) -> CharacterDetail:
246
+ """
247
+ Fetches a character detail by ID.
248
+
249
+ Parameters
250
+ ----------
251
+ id: :class:`str`
252
+ The ID of the character detail to fetch.
253
+
254
+ Returns
255
+ -------
256
+ :class:`CharacterDetail`
257
+ The character detail.
258
+ """
259
+ data = await self._request(f"avatar/{id}", use_cache=use_cache)
260
+ return CharacterDetail(**data["data"])
261
+
262
+ async def fetch_character_fetter(self, id: str, use_cache: bool = True) -> CharacterFetter:
263
+ """
264
+ Fetches a character fetter by ID.
265
+
266
+ Parameters
267
+ ----------
268
+ id: :class:`str`
269
+ The ID of the character fetter to fetch.
270
+
271
+ Returns
272
+ -------
273
+ :class:`CharacterFetter`
274
+ The character fetter.
275
+ """
276
+ data = await self._request(f"avatarFetter/{id}", use_cache=use_cache)
277
+ return CharacterFetter(**data["data"])
278
+
279
+ async def fetch_foods(self, use_cache: bool = True) -> list[Food]:
280
+ """
281
+ Fetches all foods.
282
+
283
+ Returns
284
+ -------
285
+ List[:class:`Food`]
286
+ The foods.
287
+ """
288
+ data = await self._request("food", use_cache=use_cache)
289
+ return [Food(**food) for food in data["data"]["items"].values()]
290
+
291
+ async def fetch_food_detail(self, id: int, use_cache: bool = True) -> FoodDetail:
292
+ """
293
+ Fetches a food detail by ID.
294
+
295
+ Parameters
296
+ ----------
297
+ id: :class:`int`
298
+ The ID of the food detail to fetch.
299
+
300
+ Returns
301
+ -------
302
+ :class:`FoodDetail`
303
+ The food detail.
304
+ """
305
+ data = await self._request(f"food/{id}", use_cache=use_cache)
306
+ return FoodDetail(**data["data"])
307
+
308
+ async def fetch_furnitures(self, use_cache: bool = True) -> list[Furniture]:
309
+ """
310
+ Fetches all furnitures.
311
+
312
+ Returns
313
+ -------
314
+ List[:class:`Furniture`]
315
+ The furnitures.
316
+ """
317
+ data = await self._request("furniture", use_cache=use_cache)
318
+ return [Furniture(**furniture) for furniture in data["data"]["items"].values()]
319
+
320
+ async def fetch_furniture_detail(self, id: int, use_cache: bool = True) -> FurnitureDetail:
321
+ """
322
+ Fetches a furniture detail by ID.
323
+
324
+ Parameters
325
+ ----------
326
+ id: :class:`int`
327
+ The ID of the furniture detail to fetch.
328
+
329
+ Returns
330
+ -------
331
+ :class:`FurnitureDetail`
332
+ The furniture detail.
333
+ """
334
+ data = await self._request(f"furniture/{id}", use_cache=use_cache)
335
+ return FurnitureDetail(**data["data"])
336
+
337
+ async def fetch_furniture_sets(self, use_cache: bool = True) -> list[FurnitureSet]:
338
+ """
339
+ Fetches all furniture sets.
340
+
341
+ Returns
342
+ -------
343
+ List[:class:`FurnitureSet`]
344
+ The furniture sets.
345
+ """
346
+ data = await self._request("furnitureSuite", use_cache=use_cache)
347
+ return [FurnitureSet(**furniture_set) for furniture_set in data["data"]["items"].values()]
348
+
349
+ async def fetch_furniture_set_detail(
350
+ self, id: int, use_cache: bool = True
351
+ ) -> FurnitureSetDetail:
352
+ """
353
+ Fetches a furniture set detail by ID.
354
+
355
+ Parameters
356
+ ----------
357
+ id: :class:`int`
358
+ The ID of the furniture set detail to fetch.
359
+
360
+ Returns
361
+ -------
362
+ :class:`FurnitureSetDetail`
363
+ The furniture set detail.
364
+ """
365
+ data = await self._request(f"furnitureSuite/{id}", use_cache=use_cache)
366
+ return FurnitureSetDetail(**data["data"])
367
+
368
+ async def fetch_materials(self, use_cache: bool = True) -> list[Material]:
369
+ """
370
+ Fetches all materials.
371
+
372
+ Returns
373
+ -------
374
+ List[:class:`Material`]
375
+ The materials.
376
+ """
377
+ data = await self._request("material", use_cache=use_cache)
378
+ return [Material(**material) for material in data["data"]["items"].values()]
379
+
380
+ async def fetch_material_detail(self, id: int, use_cache: bool = True) -> MaterialDetail:
381
+ """
382
+ Fetches a material detail by ID.
383
+
384
+ Parameters
385
+ ----------
386
+ id: :class:`int`
387
+ The ID of the material detail to fetch.
388
+
389
+ Returns
390
+ -------
391
+ :class:`MaterialDetail`
392
+ The material detail.
393
+ """
394
+ data = await self._request(f"material/{id}", use_cache=use_cache)
395
+ return MaterialDetail(**data["data"])
396
+
397
+ async def fetch_monsters(self, use_cache: bool = True) -> list[Monster]:
398
+ """
399
+ Fetches all monsters.
400
+
401
+ Returns
402
+ -------
403
+ List[:class:`Monster`]
404
+ The monsters.
405
+ """
406
+ data = await self._request("monster", use_cache=use_cache)
407
+ return [Monster(**monster) for monster in data["data"]["items"].values()]
408
+
409
+ async def fetch_monster_detail(self, id: int, use_cache: bool = True) -> MonsterDetail:
410
+ """
411
+ Fetches a monster detail by ID.
412
+
413
+ Parameters
414
+ ----------
415
+ id: :class:`int`
416
+ The ID of the monster detail to fetch.
417
+
418
+ Returns
419
+ -------
420
+ :class:`MonsterDetail`
421
+ The monster detail.
422
+ """
423
+ data = await self._request(f"monster/{id}", use_cache=use_cache)
424
+ return MonsterDetail(**data["data"])
425
+
426
+ async def fetch_namecards(self, use_cache: bool = True) -> list[Namecard]:
427
+ """
428
+ Fetches all name cards.
429
+
430
+ Returns
431
+ -------
432
+ List[:class:`NameCard`]
433
+ The name cards.
434
+ """
435
+ data = await self._request("namecard", use_cache=use_cache)
436
+ return [Namecard(**name_card) for name_card in data["data"]["items"].values()]
437
+
438
+ async def fetch_namecard_detail(self, id: int, use_cache: bool = True) -> NamecardDetail:
439
+ """
440
+ Fetches a name card detail by ID.
441
+
442
+ Parameters
443
+ ----------
444
+ id: :class:`int`
445
+ The ID of the name card detail to fetch.
446
+
447
+ Returns
448
+ -------
449
+ :class:`NameCardDetail`
450
+ The name card detail.
451
+ """
452
+ data = await self._request(f"namecard/{id}", use_cache=use_cache)
453
+ return NamecardDetail(**data["data"])
454
+
455
+ async def fetch_quests(self, use_cache: bool = True) -> list[Quest]:
456
+ """
457
+ Fetches all quests.
458
+
459
+ Returns
460
+ -------
461
+ List[:class:`Quest`]
462
+ The quests.
463
+ """
464
+ data = await self._request("quest", use_cache=use_cache)
465
+ return [Quest(**quest) for quest in data["data"]["items"].values()]
466
+
467
+ async def fetch_tcg_cards(self, use_cache: bool = True) -> list[TCGCard]:
468
+ """
469
+ Fetches all TCG cards.
470
+
471
+ Returns
472
+ -------
473
+ List[:class:`TCGCard`]
474
+ The TCG cards.
475
+ """
476
+ data = await self._request("gcg", use_cache=use_cache)
477
+ return [TCGCard(**tcg_card) for tcg_card in data["data"]["items"].values()]
478
+
479
+ async def fetch_tcg_card_detail(self, id: int, use_cache: bool = True) -> TCGCardDetail:
480
+ """
481
+ Fetches a TCG card detail by ID.
482
+
483
+ Parameters
484
+ ----------
485
+ id: :class:`int`
486
+ The ID of the TCG card detail to fetch.
487
+
488
+ Returns
489
+ -------
490
+ :class:`TCGCardDetail`
491
+ The TCG card detail.
492
+ """
493
+ data = await self._request(f"gcg/{id}", use_cache=use_cache)
494
+ return TCGCardDetail(**data["data"])
495
+
496
+ async def fetch_weapons(self, use_cache: bool = True) -> list[Weapon]:
497
+ """
498
+ Fetches all weapons.
499
+
500
+ Returns
501
+ -------
502
+ List[:class:`Weapon`]
503
+ The weapons.
504
+ """
505
+ data = await self._request("weapon", use_cache=use_cache)
506
+ return [Weapon(**weapon) for weapon in data["data"]["items"].values()]
507
+
508
+ async def fetch_weapon_types(self, use_cache: bool = True) -> dict[str, str]:
509
+ """
510
+ Fetches all weapon types.
511
+
512
+ Returns
513
+ -------
514
+ Dict[:class:`str`, :class:`str`]
515
+ All of the weapon types.
516
+ """
517
+ data = await self._request("weapon", use_cache=use_cache)
518
+ return data["data"]["types"]
519
+
520
+ async def fetch_weapon_detail(self, id: int, use_cache: bool = True) -> WeaponDetail:
521
+ """
522
+ Fetches a weapon detail by ID.
523
+
524
+ Parameters
525
+ ----------
526
+ id: :class:`int`
527
+ The ID of the weapon detail to fetch.
528
+
529
+ Returns
530
+ -------
531
+ :class:`WeaponDetail`
532
+ The weapon detail.
533
+ """
534
+ data = await self._request(f"weapon/{id}", use_cache=use_cache)
535
+ return WeaponDetail(**data["data"])
536
+
537
+ async def fetch_domains(self, use_cache: bool = True) -> Domains:
538
+ """
539
+ Fetches all domains.
540
+
541
+ Returns
542
+ -------
543
+ :class:`Domains`
544
+ The domains.
545
+ """
546
+ data = await self._request("dailyDungeon", use_cache=use_cache)
547
+ return Domains(**data["data"])
548
+
549
+ async def fetch_changelogs(self, use_cache: bool = True) -> list[Changelog]:
550
+ """
551
+ Fetch changelogs from the API.
552
+
553
+ Returns
554
+ -------
555
+ List[Changelog]
556
+ A list of Changelog objects.
557
+ """
558
+ data = await self._request("changelog", static=True, use_cache=use_cache)
559
+ changelogs: list[Changelog] = []
560
+ for changelog_id, log in data["data"].items():
561
+ changelogs.append(Changelog(id=int(changelog_id), **log))
562
+ return changelogs
563
+
564
+ async def fetch_upgrade_data(self, use_cache: bool = True) -> UpgradeData:
565
+ """
566
+ Fetch upgrade data from the API.
567
+
568
+ Returns
569
+ -------
570
+ UpgradeData
571
+ The upgrade data.
572
+ """
573
+ data = await self._request("upgrade", use_cache=use_cache)
574
+ return UpgradeData(**data["data"])
575
+
576
+ async def fetch_manual_weapon(self, use_cache: bool = True) -> dict[str, str]:
577
+ """
578
+ Fetch manual weapon data from the API.
579
+
580
+ Returns
581
+ -------
582
+ Dict[str, str]
583
+ The manual weapon data.
584
+ """
585
+ data = await self._request("manualWeapon", use_cache=use_cache)
586
+ return data["data"]
587
+
588
+ async def fetch_readable(self, id: str, use_cache: bool = True) -> str:
589
+ """
590
+ Fetch a readable from the API.
591
+
592
+ Parameters
593
+ ----------
594
+ id: :class:`str`
595
+ The ID of the readable to fetch.
596
+
597
+ Returns
598
+ -------
599
+ :class:`str`
600
+ The readable.
601
+ """
602
+ data = await self._request(f"readable/{id}", use_cache=use_cache)
603
+ return remove_html_tags(data["data"])
604
+
605
+ async def fetch_avatar_curve(
606
+ self, use_cache: bool = True
607
+ ) -> dict[str, dict[str, dict[str, float]]]:
608
+ """
609
+ Fetch avatar curve from the API.
610
+
611
+ Returns
612
+ -------
613
+ Dict[str, Dict[str, Dict[str, float]]]
614
+ The avatar curve.
615
+ """
616
+ data = await self._request("avatarCurve", static=True, use_cache=use_cache)
617
+ return data["data"]
618
+
619
+ async def fetch_weapon_curve(
620
+ self, use_cache: bool = True
621
+ ) -> dict[str, dict[str, dict[str, float]]]:
622
+ """
623
+ Fetch weapon curve from the API.
624
+
625
+ Returns
626
+ -------
627
+ Dict[str, Dict[str, Dict[str, float]]]
628
+ The weapon curve.
629
+ """
630
+ data = await self._request("weaponCurve", static=True, use_cache=use_cache)
631
+ return data["data"]
632
+
633
+ async def fetch_monster_curve(
634
+ self, use_cache: bool = True
635
+ ) -> dict[str, dict[str, dict[str, float]]]:
636
+ """
637
+ Fetch monster curve from the API.
638
+
639
+ Returns
640
+ -------
641
+ Dict[str, Dict[str, Dict[str, float]]]
642
+ The monster curve.
643
+ """
644
+ data = await self._request("monsterCurve", static=True, use_cache=use_cache)
645
+ return data["data"]
646
+
647
+ async def fetch_abyss_data(self, use_cache: bool = True) -> AbyssResponse:
648
+ """
649
+ Fetches abyss data from the API.
650
+
651
+ Returns
652
+ -------
653
+ AbyssResponse
654
+ The abyss data.
655
+ """
656
+ data = await self._request("tower", use_cache=use_cache)
657
+ return AbyssResponse(**data["data"])
ambr/exceptions.py ADDED
@@ -0,0 +1,25 @@
1
+ __all__ = ("AmbrAPIError", "DataNotFoundError", "ConnectionTimeoutError")
2
+
3
+
4
+ class AmbrAPIError(Exception):
5
+ def __init__(self, code: int) -> None:
6
+ self.code = code
7
+
8
+ def __str__(self) -> str:
9
+ return f"An error occurred while requesting the API, status code: {self.code}"
10
+
11
+
12
+ class DataNotFoundError(AmbrAPIError):
13
+ def __init__(self) -> None:
14
+ self.code = 404
15
+
16
+ def __str__(self) -> str:
17
+ return "Data not found"
18
+
19
+
20
+ class ConnectionTimeoutError(AmbrAPIError):
21
+ def __init__(self) -> None:
22
+ self.code = 522
23
+
24
+ def __str__(self) -> str:
25
+ return "Connection to the API timed out"
@@ -0,0 +1,17 @@
1
+ from .abyss import *
2
+ from .achievement import *
3
+ from .artifact import *
4
+ from .book import *
5
+ from .changelog import *
6
+ from .character import *
7
+ from .character_fetter import *
8
+ from .domain import *
9
+ from .food import *
10
+ from .furniture import *
11
+ from .material import *
12
+ from .monster import *
13
+ from .name_card import *
14
+ from .quest import *
15
+ from .tcg import *
16
+ from .upgrade import *
17
+ from .weapon import *