async def get_page_messages( self, sent_or_inbox: str, page: int, *, exclude: Tuple[Type[BaseException]] = (), client: Client, ) -> List[ExtDict]: assert sent_or_inbox in ("inbox", "sent") inbox = 0 if sent_or_inbox != "sent" else 1 payload = ( Params() .create_new() .put_definer("accountid", client.account_id) .put_password(client.encodedpass) .put_page(page) .put_total(0) .get_sent(inbox) .finish() ) codes = {-1: MissingAccess("Failed to get messages."), -2: NothingFound("gd.Message")} resp = await self.http.request( Route.GET_PRIVATE_MESSAGES, payload, error_codes=codes, exclude=exclude ) resp = Parser().split("#").take(0).check_empty().split("|").parse(resp) if resp is None: return [] parser = Parser().with_split(":").should_map() return list(map(parser.parse, resp))
async def get_gauntlets(self) -> List[ExtDict]: payload = Params().create_new().finish() resp = await self.http.request(Route.GET_GAUNTLETS, payload) splitted = Parser().split("#").take(0).split("|").parse(resp) parser = Parser().with_split(":").should_map() return list(map(parser.parse, filter(is_not_empty, splitted)))
async def get_level_info(self, level_id: int = 0) -> Tuple[ExtDict, ExtDict, ExtDict]: # level data, creator, song assert level_id >= -2, "Invalid Level ID provided." if level_id < 0: type, number, cooldown = await self.get_timely_info(level_id) else: type, number, cooldown = 0, -1, -1 ext = {"101": type, "102": number, "103": cooldown} codes = {-1: MissingAccess(f"Failed to get a level. Given ID: {level_id}")} payload = Params().create_new().put_definer("levelid", level_id).finish() resp = await self.http.request(Route.DOWNLOAD_LEVEL, payload, error_codes=codes) level_data = Parser().split("#").take(0).split(":").add_ext(ext).should_map().parse(resp) real_id = level_data.getcast(Index.LEVEL_ID, 0, int) payload = ( Params() .create_new() .put_definer("search", real_id) .put_filters(Filters.setup_empty()) .finish() ) resp = await self.http.request(Route.LEVEL_SEARCH, payload, error_codes=codes) if not resp or resp.count("#") < 2: raise codes.get(-1) data = resp.split("#") # getting song song_data = data[2] if not song_data: song = Converter.to_normal_song(level_data.getcast(Index.LEVEL_AUDIO_TRACK, 0, int)) else: song = Parser().with_split("~|~").should_map().parse(song_data) # getting creator creator_data = data[1] if not creator_data: id, name, account_id = (0, "unknown", 0) else: id, name, account_id = creator_data.split(":") creator = ExtDict(id=id, name=name, account_id=account_id) return level_data, creator, song
async def get_level_comments( self, level_id: int, strategy: CommentStrategy, amount: int, exclude: Tuple[Type[BaseException]] = (), ) -> List[Tuple[ExtDict, ExtDict]]: # comment, user payload = ( Params() .create_new() .put_definer("levelid", level_id) .put_page(0) .put_total(0) .put_mode(strategy.value) .put_count(amount) .finish() ) codes = { -1: MissingAccess(f"Failed to get comments of a level by ID: {level_id!r}."), -2: NothingFound("gd.Comment"), } resp = await self.http.request( Route.GET_COMMENTS, payload, error_codes=codes, exclude=exclude ) if resp is None: return [] splitted = Parser().split("#").take(0).split("|").parse(resp) parser = Parser().with_split("~").should_map() res = [] for elem in filter(is_not_empty, splitted): com_data, user_data, *_ = map(parser.parse, elem.split(":")) com_data.update({"1": level_id, "101": 0, "102": 0}) user_data = ExtDict( account_id=user_data.getcast(Index.USER_ACCOUNT_ID, 0, int), id=com_data.getcast(Index.COMMENT_AUTHOR_ID, 0, int), name=user_data.get(Index.USER_NAME, "unknown"), ) res.append((com_data, user_data)) return res
async def get_user_list( self, type: int = 0, *, exclude: Tuple[Type[BaseException]] = (), client: Client ) -> List[ExtDict]: payload = ( Params() .create_new() .put_definer("accountid", client.account_id) .put_password(client.encodedpass) .put_type(type) .finish() ) codes = { -1: MissingAccess("Failed to fetch a user list."), -2: NothingFound("gd.AbstractUser"), } resp = await self.http.request( Route.GET_USER_LIST, payload, error_codes=codes, exclude=exclude ) if resp is None: return [] resp, parser = resp.split("|"), Parser().with_split(":").should_map() return list(map(parser.parse, resp))
async def read_message( self, typeof: MessageOrRequestType, message_id: int, client: Client ) -> str: payload = ( Params() .create_new() .put_definer("accountid", client.account_id) .put_definer("messageid", message_id) .put_is_sender(typeof.name.lower()) .put_password(client.encodedpass) .finish() ) codes = {-1: MissingAccess(f"Failed to read a message by ID: {message_id!r}.")} resp = await self.http.request(Route.READ_PRIVATE_MESSAGE, payload, error_codes=codes,) mapped = Parser().with_split(":").should_map().parse(resp) return Coder.decode(type="message", string=mapped.get(Index.MESSAGE_BODY, ""))
async def get_song(self, song_id: int = 0) -> ExtDict: payload = Params().create_new().put_definer("song", song_id).finish() codes = { -1: MissingAccess(f"No songs were found with ID: {song_id}."), -2: SongRestrictedForUsage(song_id), } resp = await self.http.request(Route.GET_SONG_INFO, payload, error_codes=codes) return Parser().with_split("~|~").should_map().parse(resp)
async def get_page_map_packs( self, page: int = 0, *, exclude: Tuple[Type[BaseException]] = (), ) -> List[ExtDict]: payload = Params().create_new().put_page(page).finish() resp = await self.http.request(Route.GET_MAP_PACKS, payload) splitted = Parser().split("#").take(0).split("|").check_empty().should_map().parse(resp) if not splitted: if issubclass(NothingFound, exclude): return [] raise NothingFound("gd.MapPack") parser = Parser().with_split(":").should_map() return list(map(parser.parse, splitted))
async def search_user(self, query: Union[int, str], return_abstract: bool = False) -> ExtDict: payload = ( Params().create_new().put_definer("search", query).put_total(0).put_page(0).finish() ) codes = {-1: MissingAccess(f"Searching for {query!r} failed.")} resp = await self.http.request(Route.USER_SEARCH, payload, error_codes=codes) mapped = Parser().split("#").take(0).check_empty().split(":").should_map().parse(resp) if mapped is None: raise codes.get(-1) account_id = mapped.getcast(Index.USER_ACCOUNT_ID, 0, int) if return_abstract or not account_id: return mapped # ok; if we should not return abstract, let's find all other parameters payload = Params().create_new().put_definer("user", account_id).finish() resp = await self.http.request(Route.GET_USER_INFO, payload, error_codes=codes) mapped.update(Parser().with_split(":").should_map().parse(resp)) return mapped
async def get_user(self, account_id: int = 0, return_only_stats: bool = False) -> ExtDict: payload = Params().create_new().put_definer("user", account_id).finish() codes = {-1: MissingAccess(f"No users were found with ID: {account_id}.")} resp = await self.http.request(Route.GET_USER_INFO, payload, error_codes=codes) mapped = Parser().with_split(":").should_map().parse(resp) if return_only_stats: return mapped another = ( Params() .create_new() .put_definer("search", mapped.getcast(Index.USER_PLAYER_ID, 0, int)) .put_total(0) .put_page(0) .finish() ) some_resp = await self.http.request(Route.USER_SEARCH, another) new_resp = ( Parser().split("#").take(0).check_empty().split(":").should_map().parse(some_resp) ) if new_resp is None: raise codes.get(-1) mapped.update( {k: new_resp.get(k) for k in (Index.USER_NAME, Index.USER_ICON, Index.USER_ICON_TYPE)} ) return mapped
async def get_leaderboard( self, level_id: int, strategy: LevelLeaderboardStrategy, *, client: Client ) -> List[ExtDict]: # timely_type: TimelyType, played: bool = False, timely_index: int = 0, percentage: int = 0, # jumps: int = 0, attempts: int = 0, seconds: int = 0, coins: int = 0 # rs = Coder.gen_rs() # seed = Coder.gen_level_lb_seed(jumps, percentage, seconds, played) # if str(timely_type) == 'weekly': # timely_index += 100000 # values = [ # client.account_id, level_id, percentage, seconds, jumps, attempts, # percentage, 100 - percentage, 1, coins, timely_index, rs # ] # chk = Coder.gen_chk(type='levelscore', values=values) params = ( Params() .create_new() .put_definer("accountid", client.account_id) .put_definer("levelid", level_id) .put_password(client.encodedpass) .put_type(strategy.value) ) # params.put_percent(percentage).put_chk(chk) # for index, value in enumerate(( # attempts + 8354, jumps + 3991, seconds + 4085, seed, random.randint(100, 10000), # "", rs, attempts, coins + 5819, timely_index # ), 1): # params.put_seed(value, prefix='s', suffix=index) payload = params.finish() codes = {-1: MissingAccess(f"Failed to get leaderboard of the level by ID: {level_id!r}.")} resp = await self.http.request(Route.GET_LEVEL_SCORES, payload, error_codes=codes) if not resp: return [] resp, parser = ( resp.split("|"), Parser().with_split(":").add_ext({"101": level_id}).should_map(), ) return list(map(parser.parse, filter(is_not_empty, resp)))
async def get_page_friend_requests( self, sent_or_inbox: str = "inbox", page: int = 0, *, exclude: Tuple[Type[BaseException]] = (), client: Client, ) -> List[ExtDict]: inbox = int(sent_or_inbox == "sent") payload = ( Params() .create_new() .put_definer("accountid", str(client.account_id)) .put_password(client.encodedpass) .put_page(page) .put_total(0) .get_sent(inbox) .finish() ) codes = { -1: MissingAccess(f"Failed to get friend requests on page {page}."), -2: NothingFound("gd.FriendRequest"), } resp = await self.http.request( Route.GET_FRIEND_REQUESTS, payload, error_codes=codes, exclude=exclude ) splitted = Parser().split("#").take(0).split("|").check_empty().parse(resp) if resp is None: return [] parser = Parser().split(":").add_ext({"101": inbox}).should_map() return list(map(parser.parse, splitted))
async def retrieve_page_comments( self, account_id: int, id: int, type: str = "profile", page: int = 0, *, strategy: CommentStrategy, exclude: Tuple[Type[BaseException]] = (), ) -> List[ExtDict]: assert isinstance(page, int) and page >= 0 assert type in ("profile", "level") is_level = type == "level" typeid = is_level ^ 1 definer = "userid" if is_level else "accountid" selfid = id if is_level else account_id route = Route.GET_COMMENT_HISTORY if is_level else Route.GET_ACC_COMMENTS parser = Parser().add_ext({"101": typeid}).should_map() if is_level: parser.split(":").take(0).split("~") else: parser.with_split("~") param_obj = Params().create_new().put_definer(definer, selfid).put_page(page).put_total(0) if is_level: param_obj.put_mode(strategy.value) payload = param_obj.finish() codes = { -1: MissingAccess(f"Failed to retrieve comment for user by Account ID: {account_id!r}.") } resp = await self.http.request(route, payload, error_codes=codes, exclude=exclude) if not resp: return [] splitted = resp.split("#").pop(0) if not splitted: if issubclass(NothingFound, exclude): return [] raise NothingFound("gd.Comment") return list(map(parser.parse, filter(is_not_empty, splitted.split("|"))))
async def get_top( self, strategy: LeaderboardStrategy, count: int, *, client: Client ) -> List[ExtDict]: needs_login = strategy.value in (1, 2) # special case: map 'players' -> 'top' strategy = strategy.name.lower() if strategy.value else "top" params = Params().create_new().put_type(strategy).put_count(count) codes = {-1: MissingAccess(f"Failed to fetch leaderboard for strategy: {strategy!r}.")} if needs_login: check_logged_obj(client, "get_top") params.put_definer("accountid", client.account_id).put_password(client.encodedpass) payload = params.finish() resp = await self.http.request(Route.GET_USER_TOP, payload, error_codes=codes) resp, parser = resp.split("|"), Parser().with_split(":").should_map() return list(map(parser.parse, filter(is_not_empty, resp)))
async def search_levels_on_page( self, page: int = 0, query: str = "", filters: Optional[Filters] = None, user_id: Optional[int] = None, gauntlet: Optional[int] = None, *, exclude: Tuple[Type[BaseException]] = (), client: Client, ) -> Tuple[List[ExtDict], List[ExtDict], List[ExtDict]]: # levels, creators, songs if filters is None: filters = Filters.setup_empty() params = ( Params() .create_new() .put_definer("search", query) .put_page(page) .put_total(0) .put_filters(filters) ) codes = {-1: MissingAccess("No levels were found.")} if filters.strategy == SearchStrategy.BY_USER: if user_id is None: check_logged_obj(client, "search_levels_on_page(...)") user_id = client.id params.put_definer("accountid", client.account_id).put_password(client.encodedpass) params.put_local(1) params.put_definer("search", user_id) # override the 'str' parameter in request elif filters.strategy == SearchStrategy.FRIENDS: check_logged_obj(client, "search_levels_on_page(..., client=client)") params.put_definer("accountid", client.account_id).put_password(client.encodedpass) if gauntlet is not None: params.put_definer("gauntlet", gauntlet) payload = params.finish() resp = await self.http.request( Route.LEVEL_SEARCH, payload, exclude=exclude, error_codes=codes ) if not resp: return [], [], [] resp, parser = resp.split("#"), Parser().with_split("~|~").should_map() lvdata, cdata, sdata = resp[:3] songs = list(map(parser.parse, filter(is_not_empty, sdata.split("~:~")))) creators = [ ExtDict(zip(("id", "name", "account_id"), creator.split(":"))) for creator in filter(is_not_empty, cdata.split("|")) ] parser.with_split(":").add_ext({"101": 0, "102": -1, "103": -1}) levels = list(map(parser.parse, filter(is_not_empty, lvdata.split("|")))) return levels, creators, songs