osu.py 2.2.0__tar.gz → 2.2.2__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.
- {osu_py-2.2.0/osu.py.egg-info → osu_py-2.2.2}/PKG-INFO +1 -1
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/__init__.py +1 -1
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/asyncio/client.py +5 -1
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/client.py +8 -1
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/enums.py +2 -4
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/achievement.py +9 -9
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/beatmap.py +92 -92
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/beatmapset_event.py +25 -25
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/build.py +25 -26
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/chat.py +12 -12
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/comment.py +23 -23
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/current_user_attributes.py +24 -24
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/discussion.py +36 -36
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/event.py +36 -36
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/forum.py +28 -28
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/group.py +8 -8
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/kudosu.py +11 -11
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/match.py +26 -26
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/multiplayer.py +48 -48
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/news.py +9 -9
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/notification.py +62 -62
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/ranking.py +11 -12
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/score.py +49 -50
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/seasonal_background.py +5 -6
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/user.py +108 -108
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/wiki.py +11 -12
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/util.py +16 -2
- {osu_py-2.2.0 → osu_py-2.2.2/osu.py.egg-info}/PKG-INFO +1 -1
- {osu_py-2.2.0 → osu_py-2.2.2}/LICENSE +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/MANIFEST.in +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/README.rst +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/asyncio/__init__.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/asyncio/http.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/auth.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/constants.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/exceptions.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/http.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/notification.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/__init__.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/objects/scope.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/path.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu/results.py +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu.py.egg-info/SOURCES.txt +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu.py.egg-info/dependency_links.txt +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu.py.egg-info/requires.txt +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/osu.py.egg-info/top_level.txt +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/pyproject.toml +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/requirements.txt +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/setup.cfg +0 -0
- {osu_py-2.2.0 → osu_py-2.2.2}/setup.py +0 -0
|
@@ -1449,6 +1449,9 @@ class AsynchronousClient:
|
|
|
1449
1449
|
User default mode will be used if not specified.
|
|
1450
1450
|
|
|
1451
1451
|
key: Optional[:class:`str`]
|
|
1452
|
+
**DEPRECATED**
|
|
1453
|
+
It's recommended to prefix usernames with @ instead of setting key
|
|
1454
|
+
|
|
1452
1455
|
Type of user passed in url parameter. Can be either `id` or `username`
|
|
1453
1456
|
to limit lookup by their respective type. Passing empty or invalid
|
|
1454
1457
|
value will result in id lookup followed by username lookup if not found.
|
|
@@ -1466,7 +1469,8 @@ class AsynchronousClient:
|
|
|
1466
1469
|
`user_achievements`.
|
|
1467
1470
|
"""
|
|
1468
1471
|
mode = parse_enum_args(mode)
|
|
1469
|
-
|
|
1472
|
+
user = f"@{user}" if key is not None and key.lower() == "username" else user
|
|
1473
|
+
return User(await self.http.make_request(Path.get_user(user, mode)))
|
|
1470
1474
|
|
|
1471
1475
|
async def get_users(self, ids: Sequence[int]) -> List[UserCompact]:
|
|
1472
1476
|
"""
|
|
@@ -1424,6 +1424,9 @@ class Client:
|
|
|
1424
1424
|
User default mode will be used if not specified.
|
|
1425
1425
|
|
|
1426
1426
|
key: Optional[:class:`str`]
|
|
1427
|
+
**DEPRECATED**
|
|
1428
|
+
It's recommended to prefix usernames with @ instead of setting key
|
|
1429
|
+
|
|
1427
1430
|
Type of user passed in url parameter. Can be either `id` or `username`
|
|
1428
1431
|
to limit lookup by their respective type. Passing empty or invalid
|
|
1429
1432
|
value will result in id lookup followed by username lookup if not found.
|
|
@@ -1441,7 +1444,8 @@ class Client:
|
|
|
1441
1444
|
`user_achievements`.
|
|
1442
1445
|
"""
|
|
1443
1446
|
mode = parse_enum_args(mode)
|
|
1444
|
-
|
|
1447
|
+
user = f"@{user}" if key is not None and key.lower() == "username" else user
|
|
1448
|
+
return User(self.http.make_request(Path.get_user(user, mode)))
|
|
1445
1449
|
|
|
1446
1450
|
def get_users(self, ids: Sequence[int]) -> List[UserCompact]:
|
|
1447
1451
|
"""
|
|
@@ -1461,6 +1465,9 @@ class Client:
|
|
|
1461
1465
|
Includes attributes: country, cover, groups, statistics_rulesets.
|
|
1462
1466
|
"""
|
|
1463
1467
|
res = self.http.make_request(Path.get_users(), **{"ids[]": ids})
|
|
1468
|
+
import json
|
|
1469
|
+
with open("users.json", "w", encoding="utf-8") as f:
|
|
1470
|
+
json.dump(res["users"], f)
|
|
1464
1471
|
return list(map(UserCompact, res["users"]))
|
|
1465
1472
|
|
|
1466
1473
|
def get_wiki_page(self, locale: str, path: str) -> WikiPage:
|
|
@@ -460,8 +460,7 @@ class GameModeStr(Enum):
|
|
|
460
460
|
CATCH = "fruits"
|
|
461
461
|
MANIA = "mania"
|
|
462
462
|
|
|
463
|
-
|
|
464
|
-
def get_int_equivalent(gamemode: Enum) -> IntEnum:
|
|
463
|
+
def get_int_equivalent(self: "GameModeStr") -> "GameModeInt":
|
|
465
464
|
return GameModeInt[gamemode.name]
|
|
466
465
|
|
|
467
466
|
|
|
@@ -485,8 +484,7 @@ class GameModeInt(IntEnum):
|
|
|
485
484
|
CATCH = 2
|
|
486
485
|
MANIA = 3
|
|
487
486
|
|
|
488
|
-
|
|
489
|
-
def get_str_equivalent(gamemode: IntEnum) -> Enum:
|
|
487
|
+
def get_str_equivalent(self: "GameModeInt") -> GameModeStr:
|
|
490
488
|
return GameModeStr[gamemode.name]
|
|
491
489
|
|
|
492
490
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
|
-
from ..util import prettify, get_optional
|
|
3
|
+
from ..util import prettify, get_optional, get_required
|
|
4
4
|
from ..enums import GameModeStr
|
|
5
5
|
|
|
6
6
|
|
|
@@ -40,15 +40,15 @@ class Achievement:
|
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
def __init__(self, data):
|
|
43
|
-
self.icon_url: str = data
|
|
44
|
-
self.id: int = data
|
|
45
|
-
self.name: str = data
|
|
46
|
-
self.grouping: str = data
|
|
47
|
-
self.ordering: int = data
|
|
48
|
-
self.slug: str = data
|
|
49
|
-
self.description: str = data
|
|
43
|
+
self.icon_url: str = get_required(data, "icon_url")
|
|
44
|
+
self.id: int = get_required(data, "id")
|
|
45
|
+
self.name: str = get_required(data, "name")
|
|
46
|
+
self.grouping: str = get_required(data, "grouping")
|
|
47
|
+
self.ordering: int = get_required(data, "ordering")
|
|
48
|
+
self.slug: str = get_required(data, "slug")
|
|
49
|
+
self.description: str = get_required(data, "description")
|
|
50
50
|
self.mode: GameModeStr = get_optional(data, "mode", GameModeStr)
|
|
51
|
-
self.instructions: Optional[str] = data
|
|
51
|
+
self.instructions: Optional[str] = get_required(data, "instructions")
|
|
52
52
|
|
|
53
53
|
def __repr__(self):
|
|
54
54
|
return prettify(self, "name", "description")
|
|
@@ -2,7 +2,7 @@ from dateutil import parser
|
|
|
2
2
|
from typing import Dict, Optional, List, Union, TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from ..enums import RankStatus, GameModeStr, GameModeInt
|
|
5
|
-
from ..util import prettify, get_optional, get_optional_list
|
|
5
|
+
from ..util import prettify, get_optional, get_optional_list, get_required
|
|
6
6
|
from .user import UserCompact
|
|
7
7
|
from .current_user_attributes import BeatmapsetPermissions
|
|
8
8
|
|
|
@@ -138,25 +138,25 @@ class BeatmapsetCompact:
|
|
|
138
138
|
from .discussion import BeatmapsetDiscussion
|
|
139
139
|
from .beatmapset_event import BeatmapsetEvent
|
|
140
140
|
|
|
141
|
-
self.artist: str = data
|
|
142
|
-
self.artist_unicode: str = data
|
|
143
|
-
self.covers: Covers = Covers(data
|
|
144
|
-
self.creator: str = data
|
|
145
|
-
self.favourite_count: int = data
|
|
141
|
+
self.artist: str = get_required(data, "artist")
|
|
142
|
+
self.artist_unicode: str = get_required(data, "artist_unicode")
|
|
143
|
+
self.covers: Covers = Covers(get_required(data, "covers"))
|
|
144
|
+
self.creator: str = get_required(data, "creator")
|
|
145
|
+
self.favourite_count: int = get_required(data, "favourite_count")
|
|
146
146
|
self.hype: Optional[BeatmapsetRequirement] = get_optional(data, "hype", BeatmapsetRequirement)
|
|
147
|
-
self.id: int = data
|
|
148
|
-
self.nsfw: bool = data
|
|
149
|
-
self.offset: int = data
|
|
150
|
-
self.play_count: int = data
|
|
151
|
-
self.preview_url: str = data
|
|
152
|
-
self.source: str = data
|
|
153
|
-
self.spotlight: bool = data
|
|
154
|
-
self.status: RankStatus = RankStatus[data
|
|
155
|
-
self.title: str = data
|
|
156
|
-
self.title_unicode: str = data
|
|
157
|
-
self.track_id: Optional[int] = data
|
|
158
|
-
self.user_id: int = data
|
|
159
|
-
self.video: bool = data
|
|
147
|
+
self.id: int = get_required(data, "id")
|
|
148
|
+
self.nsfw: bool = get_required(data, "nsfw")
|
|
149
|
+
self.offset: int = get_required(data, "offset")
|
|
150
|
+
self.play_count: int = get_required(data, "play_count")
|
|
151
|
+
self.preview_url: str = get_required(data, "preview_url")
|
|
152
|
+
self.source: str = get_required(data, "source")
|
|
153
|
+
self.spotlight: bool = get_required(data, "spotlight")
|
|
154
|
+
self.status: RankStatus = RankStatus[get_required(data, "status").upper()]
|
|
155
|
+
self.title: str = get_required(data, "title")
|
|
156
|
+
self.title_unicode: str = get_required(data, "title_unicode")
|
|
157
|
+
self.track_id: Optional[int] = get_required(data, "track_id")
|
|
158
|
+
self.user_id: int = get_required(data, "user_id")
|
|
159
|
+
self.video: bool = get_required(data, "video")
|
|
160
160
|
|
|
161
161
|
self.background_url: str = f"https://assets.ppy.sh/beatmaps/{self.id}/covers/raw.jpg"
|
|
162
162
|
|
|
@@ -251,22 +251,22 @@ class Beatmapset(BeatmapsetCompact):
|
|
|
251
251
|
def __init__(self, data):
|
|
252
252
|
super().__init__(data)
|
|
253
253
|
|
|
254
|
-
self.availability: BeatmapsetAvailability = BeatmapsetAvailability(data
|
|
254
|
+
self.availability: BeatmapsetAvailability = BeatmapsetAvailability(get_required(data, "availability"))
|
|
255
255
|
self.beatmaps: Optional[List[Beatmap]] = get_optional_list(data, "beatmaps", Beatmap)
|
|
256
|
-
self.bpm: float = data
|
|
257
|
-
self.can_be_hyped: bool = data
|
|
256
|
+
self.bpm: float = get_required(data, "bpm")
|
|
257
|
+
self.can_be_hyped: bool = get_required(data, "can_be_hyped")
|
|
258
258
|
self.deleted_at: Optional[datetime] = get_optional(data, "deleted_at", parser.parse)
|
|
259
259
|
self.discussion_enabled: bool = True # Deprecated, all beatmapset discussions are enabled
|
|
260
|
-
self.discussion_locked: bool = data
|
|
261
|
-
self.is_scoreable: bool = data
|
|
260
|
+
self.discussion_locked: bool = get_required(data, "discussion_locked")
|
|
261
|
+
self.is_scoreable: bool = get_required(data, "is_scoreable")
|
|
262
262
|
self.last_updated: Optional[datetime] = get_optional(data, "last_updated", parser.parse)
|
|
263
|
-
self.legacy_thread_url: Optional[str] = data
|
|
264
|
-
self.nominations_summary: BeatmapsetRequirement = BeatmapsetRequirement(data
|
|
265
|
-
self.ranked: RankStatus = RankStatus(data
|
|
263
|
+
self.legacy_thread_url: Optional[str] = get_required(data, "legacy_thread_url")
|
|
264
|
+
self.nominations_summary: BeatmapsetRequirement = BeatmapsetRequirement(get_required(data, "nominations_summary"))
|
|
265
|
+
self.ranked: RankStatus = RankStatus(get_required(data, "ranked"))
|
|
266
266
|
self.ranked_date: Optional[datetime] = get_optional(data, "ranked_date", parser.parse)
|
|
267
|
-
self.storyboard: bool = data
|
|
267
|
+
self.storyboard: bool = get_required(data, "storyboard")
|
|
268
268
|
self.submitted_date: Optional[datetime] = get_optional(data, "submitted_date", parser.parse)
|
|
269
|
-
self.tags: str = data
|
|
269
|
+
self.tags: str = get_required(data, "tags")
|
|
270
270
|
|
|
271
271
|
|
|
272
272
|
class BeatmapCompact:
|
|
@@ -319,14 +319,14 @@ class BeatmapCompact:
|
|
|
319
319
|
)
|
|
320
320
|
|
|
321
321
|
def __init__(self, data):
|
|
322
|
-
self.beatmapset_id: int = data
|
|
323
|
-
self.difficulty_rating: float = data
|
|
324
|
-
self.id: int = data
|
|
325
|
-
self.mode: GameModeStr = GameModeStr(data
|
|
326
|
-
self.status: RankStatus = RankStatus[data
|
|
327
|
-
self.total_length: int = data
|
|
328
|
-
self.user_id: int = data
|
|
329
|
-
self.version: str = data
|
|
322
|
+
self.beatmapset_id: int = get_required(data, "beatmapset_id")
|
|
323
|
+
self.difficulty_rating: float = get_required(data, "difficulty_rating")
|
|
324
|
+
self.id: int = get_required(data, "id")
|
|
325
|
+
self.mode: GameModeStr = GameModeStr(get_required(data, "mode"))
|
|
326
|
+
self.status: RankStatus = RankStatus[get_required(data, "status").upper()]
|
|
327
|
+
self.total_length: int = get_required(data, "total_length")
|
|
328
|
+
self.user_id: int = get_required(data, "user_id")
|
|
329
|
+
self.version: str = get_required(data, "version")
|
|
330
330
|
|
|
331
331
|
self.beatmapset: Optional[BeatmapsetCompact] = get_optional(data, "beatmapset", BeatmapsetCompact)
|
|
332
332
|
self.checksum: Optional[str] = data.get("checksum")
|
|
@@ -412,25 +412,25 @@ class Beatmap(BeatmapCompact):
|
|
|
412
412
|
def __init__(self, data):
|
|
413
413
|
super().__init__(data)
|
|
414
414
|
|
|
415
|
-
self.accuracy: float = data
|
|
416
|
-
self.ar: float = data
|
|
415
|
+
self.accuracy: float = get_required(data, "accuracy")
|
|
416
|
+
self.ar: float = get_required(data, "ar")
|
|
417
417
|
self.beatmapset: Optional[Beatmapset] = get_optional(data, "beatmapset", Beatmapset)
|
|
418
|
-
self.bpm: float = data
|
|
419
|
-
self.convert: Optional[bool] = data
|
|
420
|
-
self.count_circles: int = data
|
|
421
|
-
self.count_sliders: int = data
|
|
422
|
-
self.count_spinners: int = data
|
|
423
|
-
self.cs: float = data
|
|
418
|
+
self.bpm: float = get_required(data, "bpm")
|
|
419
|
+
self.convert: Optional[bool] = get_required(data, "convert")
|
|
420
|
+
self.count_circles: int = get_required(data, "count_circles")
|
|
421
|
+
self.count_sliders: int = get_required(data, "count_sliders")
|
|
422
|
+
self.count_spinners: int = get_required(data, "count_spinners")
|
|
423
|
+
self.cs: float = get_required(data, "cs")
|
|
424
424
|
self.deleted_at: Optional[datetime] = get_optional(data, "deleted_at", parser.parse)
|
|
425
|
-
self.drain: float = data
|
|
426
|
-
self.hit_length: int = data
|
|
427
|
-
self.is_scoreable: bool = data
|
|
428
|
-
self.last_updated: datetime = parser.parse(data
|
|
429
|
-
self.mode_int: GameModeInt = GameModeInt(data
|
|
430
|
-
self.passcount: int = data
|
|
431
|
-
self.playcount: int = data
|
|
432
|
-
self.ranked: RankStatus = RankStatus(data
|
|
433
|
-
self.url: str = data
|
|
425
|
+
self.drain: float = get_required(data, "drain")
|
|
426
|
+
self.hit_length: int = get_required(data, "hit_length")
|
|
427
|
+
self.is_scoreable: bool = get_required(data, "is_scoreable")
|
|
428
|
+
self.last_updated: datetime = parser.parse(get_required(data, "last_updated"))
|
|
429
|
+
self.mode_int: GameModeInt = GameModeInt(get_required(data, "mode_int"))
|
|
430
|
+
self.passcount: int = get_required(data, "passcount")
|
|
431
|
+
self.playcount: int = get_required(data, "playcount")
|
|
432
|
+
self.ranked: RankStatus = RankStatus(get_required(data, "ranked"))
|
|
433
|
+
self.url: str = get_required(data, "url")
|
|
434
434
|
|
|
435
435
|
|
|
436
436
|
class MetadataAttribute:
|
|
@@ -447,8 +447,8 @@ class MetadataAttribute:
|
|
|
447
447
|
__slots__ = ("id", "name")
|
|
448
448
|
|
|
449
449
|
def __init__(self, data):
|
|
450
|
-
self.id: Optional[int] = data
|
|
451
|
-
self.name: str = data
|
|
450
|
+
self.id: Optional[int] = get_required(data, "id")
|
|
451
|
+
self.name: str = get_required(data, "name")
|
|
452
452
|
|
|
453
453
|
|
|
454
454
|
class OsuBeatmapDifficultyAttributes:
|
|
@@ -484,13 +484,13 @@ class OsuBeatmapDifficultyAttributes:
|
|
|
484
484
|
)
|
|
485
485
|
|
|
486
486
|
def __init__(self, data):
|
|
487
|
-
self.aim_difficulty: float = data
|
|
488
|
-
self.approach_rate: float = data
|
|
489
|
-
self.flashlight_difficulty: float = data
|
|
490
|
-
self.overall_difficulty: float = data
|
|
491
|
-
self.slider_factor: float = data
|
|
492
|
-
self.speed_difficulty: float = data
|
|
493
|
-
self.speed_note_count: float = data
|
|
487
|
+
self.aim_difficulty: float = get_required(data, "aim_difficulty")
|
|
488
|
+
self.approach_rate: float = get_required(data, "approach_rate")
|
|
489
|
+
self.flashlight_difficulty: float = get_required(data, "flashlight_difficulty")
|
|
490
|
+
self.overall_difficulty: float = get_required(data, "overall_difficulty")
|
|
491
|
+
self.slider_factor: float = get_required(data, "slider_factor")
|
|
492
|
+
self.speed_difficulty: float = get_required(data, "speed_difficulty")
|
|
493
|
+
self.speed_note_count: float = get_required(data, "speed_note_count")
|
|
494
494
|
|
|
495
495
|
def __repr__(self):
|
|
496
496
|
return prettify(self, "aim_difficulty", "speed_difficulty")
|
|
@@ -523,11 +523,11 @@ class TaikoBeatmapDifficultyAttributes:
|
|
|
523
523
|
)
|
|
524
524
|
|
|
525
525
|
def __init__(self, data):
|
|
526
|
-
self.stamina_difficulty: float = data
|
|
527
|
-
self.rhythm_difficulty: float = data
|
|
528
|
-
self.colour_difficulty: float = data
|
|
529
|
-
self.great_hit_window: float = data
|
|
530
|
-
self.peak_difficulty: float = data
|
|
526
|
+
self.stamina_difficulty: float = get_required(data, "stamina_difficulty")
|
|
527
|
+
self.rhythm_difficulty: float = get_required(data, "rhythm_difficulty")
|
|
528
|
+
self.colour_difficulty: float = get_required(data, "colour_difficulty")
|
|
529
|
+
self.great_hit_window: float = get_required(data, "great_hit_window")
|
|
530
|
+
self.peak_difficulty: float = get_required(data, "peak_difficulty")
|
|
531
531
|
|
|
532
532
|
def __repr__(self):
|
|
533
533
|
return prettify(self, "stamina_difficulty")
|
|
@@ -546,7 +546,7 @@ class FruitsBeatmapDifficultyAttributes:
|
|
|
546
546
|
__slots__ = "approach_rate"
|
|
547
547
|
|
|
548
548
|
def __init__(self, data):
|
|
549
|
-
self.approach_rate: float = data
|
|
549
|
+
self.approach_rate: float = get_required(data, "approach_rate")
|
|
550
550
|
|
|
551
551
|
def __repr__(self):
|
|
552
552
|
return prettify(self, "approach_rate")
|
|
@@ -567,8 +567,8 @@ class ManiaBeatmapDifficultyAttributes:
|
|
|
567
567
|
__slots__ = ("great_hit_window", "score_multiplier")
|
|
568
568
|
|
|
569
569
|
def __init__(self, data):
|
|
570
|
-
self.great_hit_window: float = data
|
|
571
|
-
self.score_multiplier: float = data
|
|
570
|
+
self.great_hit_window: float = get_required(data, "great_hit_window")
|
|
571
|
+
self.score_multiplier: float = get_required(data, "score_multiplier")
|
|
572
572
|
|
|
573
573
|
def __repr__(self):
|
|
574
574
|
return prettify(self, "great_hit_window")
|
|
@@ -607,9 +607,9 @@ class BeatmapDifficultyAttributes:
|
|
|
607
607
|
]
|
|
608
608
|
|
|
609
609
|
def __init__(self, data):
|
|
610
|
-
data = data
|
|
611
|
-
self.max_combo: int = data
|
|
612
|
-
self.star_rating: float = data
|
|
610
|
+
data = get_required(data, "attributes")
|
|
611
|
+
self.max_combo: int = get_required(data, "max_combo")
|
|
612
|
+
self.star_rating: float = get_required(data, "star_rating")
|
|
613
613
|
if "aim_difficulty" in data:
|
|
614
614
|
self.type = GameModeStr.STANDARD
|
|
615
615
|
self.mode_attributes = OsuBeatmapDifficultyAttributes(data)
|
|
@@ -687,13 +687,13 @@ class Covers:
|
|
|
687
687
|
)
|
|
688
688
|
|
|
689
689
|
def __init__(self, data):
|
|
690
|
-
self.cover: str = data
|
|
690
|
+
self.cover: str = get_required(data, "cover")
|
|
691
691
|
self.cover_2x: str = data["cover@2x"]
|
|
692
|
-
self.card: str = data
|
|
692
|
+
self.card: str = get_required(data, "card")
|
|
693
693
|
self.card_2x: str = data["card@2x"]
|
|
694
|
-
self.list: str = data
|
|
694
|
+
self.list: str = get_required(data, "list")
|
|
695
695
|
self.list_2x: str = data["list@2x"]
|
|
696
|
-
self.slimcover: str = data
|
|
696
|
+
self.slimcover: str = get_required(data, "slimcover")
|
|
697
697
|
self.slimcover_2x: str = data["slimcover@2x"]
|
|
698
698
|
|
|
699
699
|
def __repr__(self):
|
|
@@ -718,10 +718,10 @@ class BeatmapPlaycount:
|
|
|
718
718
|
__slots__ = ("beatmap_id", "beatmap", "beatmapset", "count")
|
|
719
719
|
|
|
720
720
|
def __init__(self, data):
|
|
721
|
-
self.beatmap_id: int = data
|
|
721
|
+
self.beatmap_id: int = get_required(data, "beatmap_id")
|
|
722
722
|
self.beatmap: Optional[BeatmapCompact] = get_optional(data, "beatmap", BeatmapCompact)
|
|
723
723
|
self.beatmapset: Optional[BeatmapsetCompact] = get_optional(data, "beatmapset", BeatmapsetCompact)
|
|
724
|
-
self.count: int = data
|
|
724
|
+
self.count: int = get_required(data, "count")
|
|
725
725
|
|
|
726
726
|
def __repr__(self):
|
|
727
727
|
return prettify(self, "beatmap_id", "count")
|
|
@@ -746,7 +746,7 @@ class BeatmapsetRequirement:
|
|
|
746
746
|
__slots__ = ("current", "required", "eligible_main_rulesets", "required_meta")
|
|
747
747
|
|
|
748
748
|
def __init__(self, data):
|
|
749
|
-
self.current: int = data
|
|
749
|
+
self.current: int = get_required(data, "current")
|
|
750
750
|
self.required: Optional[int] = data.get("required")
|
|
751
751
|
|
|
752
752
|
self.eligible_main_rulesets: Optional[List[GameModeStr]] = get_optional_list(
|
|
@@ -774,8 +774,8 @@ class BeatmapsetRequiredNominations:
|
|
|
774
774
|
___slots__ = ("main_ruleset", "non_main_ruleset")
|
|
775
775
|
|
|
776
776
|
def __init__(self, data):
|
|
777
|
-
self.main_ruleset = data
|
|
778
|
-
self.non_main_ruleset = data
|
|
777
|
+
self.main_ruleset = get_required(data, "main_ruleset")
|
|
778
|
+
self.non_main_ruleset = get_required(data, "non_main_ruleset")
|
|
779
779
|
|
|
780
780
|
|
|
781
781
|
class BeatmapsetAvailability:
|
|
@@ -792,7 +792,7 @@ class BeatmapsetAvailability:
|
|
|
792
792
|
__slots__ = ("download_disabled", "more_information")
|
|
793
793
|
|
|
794
794
|
def __init__(self, data):
|
|
795
|
-
self.download_disabled: bool = data
|
|
795
|
+
self.download_disabled: bool = get_required(data, "download_disabled")
|
|
796
796
|
self.more_information: Optional[str] = data.get("more_information")
|
|
797
797
|
|
|
798
798
|
def __repr__(self):
|
|
@@ -835,7 +835,7 @@ class BaseNominations:
|
|
|
835
835
|
self.nomination_reset: Optional[BeatmapsetEvent] = get_optional(data, "nomination_reset", BeatmapsetEvent)
|
|
836
836
|
self.ranking_eta: Optional[str] = data.get("ranking_eta")
|
|
837
837
|
self.ranking_queue_position: Optional[int] = data.get("ranking_queue_position")
|
|
838
|
-
self.required_hype: int = data
|
|
838
|
+
self.required_hype: int = get_required(data, "required_hype")
|
|
839
839
|
|
|
840
840
|
def __repr__(self):
|
|
841
841
|
return prettify(self, "nominated")
|
|
@@ -860,8 +860,8 @@ class LegacyNominations(BaseNominations):
|
|
|
860
860
|
|
|
861
861
|
def __init__(self, data):
|
|
862
862
|
super().__init__(data)
|
|
863
|
-
self.current: int = data
|
|
864
|
-
self.required: int = data
|
|
863
|
+
self.current: int = get_required(data, "current")
|
|
864
|
+
self.required: int = get_required(data, "required")
|
|
865
865
|
|
|
866
866
|
def __repr__(self):
|
|
867
867
|
return prettify("current", "required")
|
|
@@ -887,11 +887,11 @@ class Nominations(BaseNominations):
|
|
|
887
887
|
def __init__(self, data):
|
|
888
888
|
super().__init__(data)
|
|
889
889
|
self.current: Dict[GameModeStr, int] = dict(
|
|
890
|
-
zip(map(GameModeStr, (current := data
|
|
890
|
+
zip(map(GameModeStr, (current := get_required(data, "current")).keys()), current.values())
|
|
891
891
|
)
|
|
892
892
|
self.required: Dict[GameModeStr, int] = dict(
|
|
893
893
|
zip(
|
|
894
|
-
map(GameModeStr, (required := data
|
|
894
|
+
map(GameModeStr, (required := get_required(data, "required")).keys()),
|
|
895
895
|
required.values(),
|
|
896
896
|
)
|
|
897
897
|
)
|
|
@@ -916,10 +916,10 @@ class CurrentNomination:
|
|
|
916
916
|
"""
|
|
917
917
|
|
|
918
918
|
def __init__(self, data):
|
|
919
|
-
self.beatmapset_id: int = data
|
|
919
|
+
self.beatmapset_id: int = get_required(data, "beatmapset_id")
|
|
920
920
|
self.rulesets: Optional[List[GameModeStr]] = get_optional_list(data, "rulesets", GameModeStr)
|
|
921
|
-
self.reset: bool = data
|
|
922
|
-
self.user_id: int = data
|
|
921
|
+
self.reset: bool = get_required(data, "reset")
|
|
922
|
+
self.user_id: int = get_required(data, "user_id")
|
|
923
923
|
|
|
924
924
|
def __repr__(self):
|
|
925
925
|
return prettify(self, "beatmapset_id", "user_id")
|
|
@@ -2,7 +2,7 @@ from dateutil import parser
|
|
|
2
2
|
from typing import Optional, List, TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from ..enums import GameModeStr, BeatmapsetEventType
|
|
5
|
-
from ..util import prettify
|
|
5
|
+
from ..util import prettify, get_required
|
|
6
6
|
from .discussion import BeatmapsetDiscussion
|
|
7
7
|
from .beatmap import BeatmapsetCompact
|
|
8
8
|
|
|
@@ -78,18 +78,18 @@ class BeatmapsetEvent:
|
|
|
78
78
|
)
|
|
79
79
|
|
|
80
80
|
def __init__(self, data):
|
|
81
|
-
self.id: int = data
|
|
82
|
-
self.type: BeatmapsetEventType = BeatmapsetEventType(data
|
|
83
|
-
self.comment: Optional[BeatmapsetEventComment] = data
|
|
81
|
+
self.id: int = get_required(data, "id")
|
|
82
|
+
self.type: BeatmapsetEventType = BeatmapsetEventType(get_required(data, "type"))
|
|
83
|
+
self.comment: Optional[BeatmapsetEventComment] = get_required(data, "comment")
|
|
84
84
|
if self.comment is not None:
|
|
85
85
|
self.comment = BeatmapsetEventComment(self.comment, self.type) # type: ignore
|
|
86
|
-
self.created_at: datetime = parser.parse(data
|
|
86
|
+
self.created_at: datetime = parser.parse(get_required(data, "created_at"))
|
|
87
87
|
self.user_id: Optional[int] = data.get("user_id")
|
|
88
88
|
self.beatmapset: Optional[BeatmapsetCompact] = (
|
|
89
|
-
BeatmapsetCompact(data
|
|
89
|
+
BeatmapsetCompact(get_required(data, "beatmapset")) if data.get("beatmapset") is not None else None
|
|
90
90
|
)
|
|
91
91
|
self.discussion: Optional[BeatmapsetDiscussion] = (
|
|
92
|
-
BeatmapsetDiscussion(data
|
|
92
|
+
BeatmapsetDiscussion(get_required(data, "discussion")) if data.get("discussion") is not None else None
|
|
93
93
|
)
|
|
94
94
|
|
|
95
95
|
def __repr__(self):
|
|
@@ -106,7 +106,7 @@ class BeatmapsetEventNominate:
|
|
|
106
106
|
__slots__ = ("modes",)
|
|
107
107
|
|
|
108
108
|
def __init__(self, data):
|
|
109
|
-
self.modes: List[GameModeStr] = list(map(GameModeStr, data
|
|
109
|
+
self.modes: List[GameModeStr] = list(map(GameModeStr, get_required(data, "modes")))
|
|
110
110
|
|
|
111
111
|
def __repr__(self):
|
|
112
112
|
return prettify(self, "modes")
|
|
@@ -122,7 +122,7 @@ class BeatmapsetEventRemoveFromLoved:
|
|
|
122
122
|
__slots__ = ("reason",)
|
|
123
123
|
|
|
124
124
|
def __init__(self, data):
|
|
125
|
-
self.reason: str = data
|
|
125
|
+
self.reason: str = get_required(data, "reason")
|
|
126
126
|
|
|
127
127
|
def __repr__(self):
|
|
128
128
|
return prettify(self, "reason")
|
|
@@ -138,7 +138,7 @@ class BeatmapsetEventDisqualify:
|
|
|
138
138
|
__slots__ = ("nominator_ids",)
|
|
139
139
|
|
|
140
140
|
def __init__(self, data):
|
|
141
|
-
self.nominator_ids: List[int] = data
|
|
141
|
+
self.nominator_ids: List[int] = get_required(data, "nominator_ids")
|
|
142
142
|
|
|
143
143
|
def __repr__(self):
|
|
144
144
|
return prettify(self, "nominator_ids")
|
|
@@ -156,8 +156,8 @@ class BeatmapsetEventVote:
|
|
|
156
156
|
__slots__ = ("user_id", "score")
|
|
157
157
|
|
|
158
158
|
def __init__(self, data):
|
|
159
|
-
self.user_id: int = data
|
|
160
|
-
self.score: int = data
|
|
159
|
+
self.user_id: int = get_required(data, "user_id")
|
|
160
|
+
self.score: int = get_required(data, "score")
|
|
161
161
|
|
|
162
162
|
def __repr__(self):
|
|
163
163
|
return prettify(self, "user_id", "score")
|
|
@@ -176,10 +176,10 @@ class BeatmapsetEventKudosuChange:
|
|
|
176
176
|
|
|
177
177
|
def __init__(self, data):
|
|
178
178
|
self.new_votes: Optional[BeatmapsetEventVote] = (
|
|
179
|
-
BeatmapsetEventVote(data
|
|
179
|
+
BeatmapsetEventVote(get_required(data, "new_votes")) if data.get("new_votes") is not None else None
|
|
180
180
|
)
|
|
181
181
|
self.votes: Optional[List[BeatmapsetEventVote]] = (
|
|
182
|
-
list(map(BeatmapsetEventVote, data
|
|
182
|
+
list(map(BeatmapsetEventVote, get_required(data, "votes"))) if data.get("votes") is not None else None
|
|
183
183
|
)
|
|
184
184
|
|
|
185
185
|
def __repr__(self):
|
|
@@ -205,7 +205,7 @@ class BeatmapsetEventKudosuRecalculate:
|
|
|
205
205
|
|
|
206
206
|
def __init__(self, data):
|
|
207
207
|
self.new_votes: Optional[BeatmapsetEventVote] = (
|
|
208
|
-
BeatmapsetEventVote(data
|
|
208
|
+
BeatmapsetEventVote(get_required(data, "new_votes")) if data.get("new_votes") is not None else None
|
|
209
209
|
)
|
|
210
210
|
|
|
211
211
|
def __repr__(self):
|
|
@@ -222,7 +222,7 @@ class BeatmapsetEventDiscussionLock:
|
|
|
222
222
|
__slots__ = ("reason",)
|
|
223
223
|
|
|
224
224
|
def __init__(self, data):
|
|
225
|
-
self.reason: str = data
|
|
225
|
+
self.reason: str = get_required(data, "reason")
|
|
226
226
|
|
|
227
227
|
def __repr__(self):
|
|
228
228
|
return prettify(self, "reason")
|
|
@@ -238,7 +238,7 @@ class BeatmapsetEventNominationReset:
|
|
|
238
238
|
__slots__ = ("nominator_ids",)
|
|
239
239
|
|
|
240
240
|
def __init__(self, data):
|
|
241
|
-
self.nominator_ids: List[int] = data
|
|
241
|
+
self.nominator_ids: List[int] = get_required(data, "nominator_ids")
|
|
242
242
|
|
|
243
243
|
def __repr__(self):
|
|
244
244
|
return prettify(self, "nominator_ids")
|
|
@@ -256,8 +256,8 @@ class BeatmapsetEventNominationResetReceived:
|
|
|
256
256
|
__slots__ = ("source_user_id", "source_user_username")
|
|
257
257
|
|
|
258
258
|
def __init__(self, data):
|
|
259
|
-
self.source_user_id: int = data
|
|
260
|
-
self.source_user_username: str = data
|
|
259
|
+
self.source_user_id: int = get_required(data, "source_user_id")
|
|
260
|
+
self.source_user_username: str = get_required(data, "source_user_username")
|
|
261
261
|
|
|
262
262
|
def __repr__(self):
|
|
263
263
|
return prettify(self, "source_user_id", "source_user_username")
|
|
@@ -275,8 +275,8 @@ class BeatmapsetEventEdit:
|
|
|
275
275
|
__slots__ = ("old", "new")
|
|
276
276
|
|
|
277
277
|
def __init__(self, data):
|
|
278
|
-
self.old: str = data
|
|
279
|
-
self.new: str = data
|
|
278
|
+
self.old: str = get_required(data, "old")
|
|
279
|
+
self.new: str = get_required(data, "new")
|
|
280
280
|
|
|
281
281
|
def __repr__(self):
|
|
282
282
|
return prettify(self, "old", "new")
|
|
@@ -314,10 +314,10 @@ class BeatmapsetEventBeatmapOwnerChange:
|
|
|
314
314
|
__slots__ = ("beatmap_id", "beatmap_version", "new_user_id", "new_user_username")
|
|
315
315
|
|
|
316
316
|
def __init__(self, data):
|
|
317
|
-
self.beatmap_id: int = data
|
|
318
|
-
self.beatmap_version: str = data
|
|
319
|
-
self.new_user_id: int = data
|
|
320
|
-
self.new_user_username: str = data
|
|
317
|
+
self.beatmap_id: int = get_required(data, "beatmap_id")
|
|
318
|
+
self.beatmap_version: str = get_required(data, "beatmap_version")
|
|
319
|
+
self.new_user_id: int = get_required(data, "new_user_id")
|
|
320
|
+
self.new_user_username: str = get_required(data, "new_user_username")
|
|
321
321
|
|
|
322
322
|
def __repr__(self):
|
|
323
323
|
return prettify(self, "beatmap_id", "new_user_username")
|