diff --git a/aiosu/helpers.py b/aiosu/helpers.py index 8128624..ddfd6dc 100644 --- a/aiosu/helpers.py +++ b/aiosu/helpers.py @@ -17,11 +17,12 @@ __all__ = ( "add_param", + "append_param", "from_list", ) -def from_list(f: Callable[[Any], T], x: list[object]) -> list[T]: +def from_list(f: Callable[[Any], T], x: list) -> list[T]: r"""Applies a function to all elements in a list. :param f: Function to apply on list elements @@ -37,9 +38,29 @@ def from_list(f: Callable[[Any], T], x: list[object]) -> list[T]: return [f(y) for y in x] +def append_param( + value: object, + l: list, + append: bool = True, +) -> None: + r"""Appends a value to a list if it is not None and append is True. + + :param value: Value to append + :type value: object + :param l: List to append to + :type l: list[object] + :param append: Whether to append or not, defaults to True + :type append: bool, optional, defaults to True + """ + if value is None: + return + if append: + l.append(value) + + def add_param( params: MutableMapping[str, Any], - kwargs: Mapping[str, Any], + kwargs: Mapping[str, object], key: str, param_name: Optional[str] = None, converter: Optional[Callable[[Any], T]] = None, diff --git a/aiosu/models/beatmap.py b/aiosu/models/beatmap.py index 200a99e..60391d1 100644 --- a/aiosu/models/beatmap.py +++ b/aiosu/models/beatmap.py @@ -51,10 +51,35 @@ "BeatmapsetEventType", "BeatmapsetRequestStatus", "BeatmapsetSearchResponse", + "BeatmapsetSortType", "BeatmapsetVoteEvent", "UserBeatmapType", ) +BeatmapsetSortType = Literal[ + "title_asc", + "title_desc", + "artist_asc", + "artist_desc", + "difficulty_asc", + "difficulty_desc", + "ranked_asc", + "ranked_desc", + "rating_asc", + "rating_desc", + "plays_asc", + "plays_desc", + "favourites_asc", + "favourites_desc", +] + +BeatmapsetBundleFilterType = Literal[ + "any", + "currently", + "previously", + "never", +] + BeatmapsetDisscussionType = Literal[ "hype", "praise", diff --git a/aiosu/v2/client.py b/aiosu/v2/client.py index 6609403..fa15c1a 100644 --- a/aiosu/v2/client.py +++ b/aiosu/v2/client.py @@ -25,6 +25,7 @@ from ..events import Eventable from ..exceptions import APIException from ..helpers import add_param +from ..helpers import append_param from ..helpers import from_list from ..models import ArtistResponse from ..models import Beatmap @@ -1336,7 +1337,6 @@ async def lookup_beatmapset(self, beatmap_id: int) -> Beatmapset: @requires_scope(Scopes.PUBLIC) async def search_beatmapsets( self, - search_filter: Optional[str] = "", **kwargs: Any, ) -> BeatmapsetSearchResponse: r"""Search beatmapset by filter. @@ -1347,6 +1347,36 @@ async def search_beatmapsets( See below :Keyword Arguments: + * *query* (``str``) -- + Optional, search query + * *mode* (``aiosu.models.gamemode.Gamemode``) -- + Optional, gamemode to search for + * *category* (``aiosu.models.beatmap.BeatmapsetCategory``) -- + Optional, beatmapset category + * *show_explicit* (``bool``) -- + Optional, whether to show explicit content, defaults to ``False`` + * *genre* (``aiosu.models.beatmap.BeatmapsetGenre``) -- + Optional, beatmapset genre + * *language* (``aiosu.models.beatmap.BeatmapsetLanguage``) -- + Optional, beatmapset language + * *bundled* (``aiosu.models.beatmap.BeatmapsetBundleFilterType``) -- + Optional, filter by bundled status. Currently not implemented + * *sort* (``aiosu.models.beatmap.BeatmapsetSortType``) -- + Optional, sort order + * *only_video* (``bool``) -- + Optional, whether to only show beatmapsets with video, defaults to ``False`` + * *only_storyboard* (``bool``) -- + Optional, whether to only show beatmapsets with storyboard, defaults to ``False`` + * *recommended_difficulty* (``bool``) -- + Optional, whether to only show beatmapsets with recommended difficulty, defaults to ``False`` + * *only_followed* (``bool``) -- + Optional, whether to only show beatmapsets from followed mappers, defaults to ``False`` + * *only_spotlights* (``bool``) -- + Optional, whether to only show beatmapsets were featured in spotlights, defaults to ``False`` + * *only_featured_artists* (``bool``) -- + Optional, whether to only show beatmapsets were featured artists, defaults to ``False`` + * *include_converts* (``bool``) -- + Optional, whether to include converted beatmapsets, defaults to ``False`` * *cursor_string* (``str``) -- Optional, cursor string to get the next page of results @@ -1354,9 +1384,50 @@ async def search_beatmapsets( :return: Beatmapset search response :rtype: list[aiosu.models.beatmap.BeatmapsetSearchResponse] """ - url = f"{self.base_url}/api/v2/beatmapsets/search/{search_filter}" + url = f"{self.base_url}/api/v2/beatmapsets/search" params: dict[str, object] = {} - add_param(params, kwargs, key="cursor_string") + extras: list[str] = [] + general: list[str] = [] + + append_param("video", extras, kwargs.pop("only_video", False)) + append_param("storyboard", extras, kwargs.pop("only_storyboard", False)) + + append_param( + "recommended", + general, + kwargs.pop("recommended_difficulty", False), + ) + append_param("follows", general, kwargs.pop("only_followed", False)) + append_param("spotlights", general, kwargs.pop("only_spotlights", False)) + append_param( + "featured_artists", + general, + kwargs.pop("only_featured_artists", False), + ) + append_param("converts", general, kwargs.pop("include_converts", False)) + + extras_str = ".".join(extras) + general_str = ".".join(general) + if extras_str: + params["e"] = extras_str + if general_str: + params["c"] = general_str + + add_param(params, kwargs, key="query", param_name="q") + add_param( + params, + kwargs, + key="mode", + param_name="m", + converter=lambda x: str(Gamemode(x)), + ) + add_param(params, kwargs, key="category", param_name="s") + add_param(params, kwargs, key="show_explicit", param_name="nsfw") + add_param(params, kwargs, key="genre", param_name="g") + add_param(params, kwargs, key="language", param_name="l") + add_param(params, kwargs, key="bundled") + add_param(params, kwargs, key="sort") + add_param(params, kwargs, key="cursor_string", param_name="cursor") json = await self._request("GET", url) resp = BeatmapsetSearchResponse.model_validate(json) if resp.cursor_string: diff --git a/tests/generate/main.py b/tests/generate/main.py index d507c5d..ba9b29d 100644 --- a/tests/generate/main.py +++ b/tests/generate/main.py @@ -476,8 +476,9 @@ def _register_routes(self) -> None: ) self._register_route( "GET", - f"{BASE_URL}/api/v2/beatmapsets/search/doja cat say so", + f"{BASE_URL}/api/v2/beatmapsets/search", f"{DATA_DIR}/v2/search_beatmapsets_200.json", + params={"q": "doja cat say so"}, ) self._register_route( "GET", diff --git a/tests/test_v2/test_client.py b/tests/test_v2/test_client.py index aaf459a..7e00164 100644 --- a/tests/test_v2/test_client.py +++ b/tests/test_v2/test_client.py @@ -209,7 +209,7 @@ async def test_generated(status_code, content_type, token, mocker): generate_test( aiosu.v2.Client.search_beatmapsets, STATUS_CAN_200, - func_kwargs={"search_filter": "doja cat say so"}, + func_kwargs={"query": "doja cat say so"}, ), generate_test(aiosu.v2.Client.get_beatmap_packs, STATUS_CAN_200), generate_test(