def official(cls, id: int, server_style: bool = True, *, client: Optional[Client] = None) -> Song: data = Converter.to_normal_song(id, server_style) return cls(**data, client=client)
def from_data(cls, data: ExtDict, client: Client) -> Gauntlet: try: level_ids = tuple(map(int, data.get(Index.GAUNTLET_LEVEL_IDS, "").split(","))) except ValueError: level_ids = () gid = data.getcast(Index.GAUNTLET_ID, 0, int) name = Converter.get_gauntlet_name(gid) return cls(id=gid, name=name, level_ids=level_ids, client=client)
def __repr__(self) -> str: info = { "account_id": self.account_id, "name": repr(self.name), "id": self.id, "place": Converter.to_ordinal(self.place), "stars": self.stars, "demons": self.demons, "cp": self.cp, } return make_repr(self, info)
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
def get_level_difficulty(self) -> Union[LevelDifficulty, DemonDifficulty]: """Compute actual level difficulty and return the enum.""" address = self.read_type(self.ptr_type, 0x3222D0, 0x164, 0x22C, 0x114) is_demon, is_auto, difficulty, demon_difficulty = ( self.read(Bool, address + 0x29C), self.read(Bool, address + 0x2B0), self.read(UInt32, address + 0x1E4), self.read(UInt32, address + 0x2A0), ) return Converter.convert_level_difficulty(difficulty, demon_difficulty, is_demon, is_auto)
def get_level_difficulty(self) -> Union[LevelDifficulty, DemonDifficulty]: address = self.read_bytes(self.PTR_LEN, 0x3222D0, 0x164, 0x22C, 0x114).as_int() is_demon, is_auto, diff, demon_diff = ( self.read_at(1, address + 0x29C).as_bool(), self.read_at(1, address + 0x2B0).as_bool(), self.read_at(4, address + 0x1E4).as_int(), self.read_at(4, address + 0x2A0).as_int(), ) return Converter.convert_level_difficulty( diff=diff, demon_diff=demon_diff, is_demon=is_demon, is_auto=is_auto )
def __getitem__(self, item: Any) -> Any: try: return super().__getitem__(item) except KeyError: from gd.utils.converter import Converter try: return super().__getitem__(Converter.snake_to_camel(item)) except KeyError: pass raise
async def update_profile(self, settings: Dict[str, int], *, client: Client) -> None: settings_cased = {Converter.snake_to_camel(name): value for name, value in settings.items()} rs = Coder.gen_rs() req_chk_params = [client.account_id] req_chk_params.extend( settings.get(param, 0) for param in ( "user_coins", "demons", "stars", "coins", "icon_type", "icon", "diamonds", "acc_icon", "acc_ship", "acc_ball", "acc_bird", "acc_dart", "acc_robot", "acc_glow", "acc_spider", "acc_explosion", ) ) chk = Coder.gen_chk(type="userscore", values=req_chk_params) payload = ( Params() .create_new() .put_definer("accountid", client.account_id) .put_password(client.encodedpass) .put_username(client.name) .put_seed(rs) .put_seed(chk, suffix=str(2)) .finish() ) payload.update(settings_cased) resp = await self.http.request(Route.UPDATE_USER_SCORE, payload) if not resp > 0: raise MissingAccess(f"Failed to update profile of a client: {client!r}")
def from_data(cls, data: ExtDict, client: Client) -> MapPack: try: level_ids = tuple(map(int, data.get(Index.MAP_PACK_LEVEL_IDS, "").split(","))) except ValueError: level_ids = () color_string = data.get(Index.MAP_PACK_COLOR, "255,255,255") color = Color.from_rgb(*map(int, color_string.split(","))) difficulty = Converter.value_to_pack_difficulty( data.getcast(Index.MAP_PACK_DIFFICULTY, 0, int) ) return cls( id=data.getcast(Index.MAP_PACK_ID, 0, int), name=data.get(Index.MAP_PACK_NAME, "unknown"), level_ids=level_ids, stars=data.getcast(Index.MAP_PACK_STARS, 0, int), coins=data.getcast(Index.MAP_PACK_COINS, 0, int), difficulty=difficulty, color=color, client=client, )
def from_data( cls, data: ExtDict, creator: Union[ExtDict, AbstractUser], song: Union[ExtDict, Song], client: Client, ) -> Level: if isinstance(creator, ExtDict): creator = AbstractUser(**creator, client=client) if isinstance(song, ExtDict): if any(key.isdigit() for key in song.keys()): song = Song.from_data(song, client=client) else: song = Song(**song, client=client) string = data.get(Index.LEVEL_PASS) if string is None: copyable, password = False, None else: if string == ZERO_STR: copyable, password = False, None else: try: # decode password password = Coder.decode(type="levelpass", string=string) except Exception: # failed to get password copyable, password = False, None else: copyable = True if not password: password = None else: # password is in format 1XXXXXX password = password[1:] password = int(password) if password.isdigit() else None desc = Coder.do_base64( data.get(Index.LEVEL_DESCRIPTION, ""), encode=False, errors="replace" ) level_data = data.get(Index.LEVEL_DATA, "") try: level_data = Coder.unzip(level_data) except Exception: # conversion failed pass diff = data.getcast(Index.LEVEL_DIFFICULTY, 0, int) demon_diff = data.getcast(Index.LEVEL_DEMON_DIFFICULTY, 0, int) is_demon = bool(data.getcast(Index.LEVEL_IS_DEMON, 0, int)) is_auto = bool(data.getcast(Index.LEVEL_IS_AUTO, 0, int)) difficulty = Converter.convert_level_difficulty( diff=diff, demon_diff=demon_diff, is_demon=is_demon, is_auto=is_auto ) return cls( id=data.getcast(Index.LEVEL_ID, 0, int), name=data.get(Index.LEVEL_NAME, "unknown"), description=desc, version=data.getcast(Index.LEVEL_VERSION, 0, int), creator=creator, song=song, data=level_data, password=password, copyable=copyable, is_demon=is_demon, is_auto=is_auto, low_detail_mode=bool(data.get(Index.LEVEL_HAS_LDM)), difficulty=difficulty, stars=data.getcast(Index.LEVEL_STARS, 0, int), coins=data.getcast(Index.LEVEL_COIN_COUNT, 0, int), verified_coins=bool(data.getcast(Index.LEVEL_COIN_VERIFIED, 0, int)), is_epic=bool(data.getcast(Index.LEVEL_IS_EPIC, 0, int)), original=data.getcast(Index.LEVEL_ORIGINAL, 0, int), downloads=data.getcast(Index.LEVEL_DOWNLOADS, 0, int), rating=data.getcast(Index.LEVEL_LIKES, 0, int), score=data.getcast(Index.LEVEL_FEATURED_SCORE, 0, int), uploaded_timestamp=data.get(Index.LEVEL_UPLOADED_TIMESTAMP, "unknown"), last_updated_timestamp=data.get(Index.LEVEL_LAST_UPDATED_TIMESTAMP, "unknown"), length=LevelLength.from_value(data.getcast(Index.LEVEL_LENGTH, 0, int), "XL"), game_version=data.getcast(Index.LEVEL_GAME_VERSION, 0, int), stars_requested=data.getcast(Index.LEVEL_REQUESTED_STARS, 0, int), object_count=data.getcast(Index.LEVEL_OBJECT_COUNT, 0, int), type=TimelyType.from_value(data.getcast(Index.LEVEL_TIMELY_TYPE, 0, int), 0), time_n=data.getcast(Index.LEVEL_TIMELY_INDEX, -1, int), cooldown=data.getcast(Index.LEVEL_TIMELY_COOLDOWN, -1, int), client=client, )
async def upload_level( self, data: str, name: str, level_id: int, version: int, length: LevelLength, audio_track: int, desc: str, song_id: int, is_auto: bool, original: int, two_player: bool, objects: int, coins: int, stars: int, unlisted: bool, ldm: bool, password: Optional[Union[int, str]], copyable: bool, *, client: Client, ) -> int: data = Coder.zip(data) extra_string = "_".join(map(str, (0 for _ in range(55)))) desc = Coder.do_base64(desc) upload_seed = Coder.gen_level_upload_seed(data) seed2 = Coder.gen_chk(type="level", values=[upload_seed]) seed = Coder.gen_rs() pwd = 0 if copyable and password is None: pwd = 1 check, add = str(password), 1000000 if check.isdigit() and int(check) < add: pwd = add + int(password) payload = ( Params() .create_new() .put_definer("accountid", client.account_id) .put_definer("levelid", level_id) .put_definer("song", song_id) .put_seed(seed) .put_seed(seed2, suffix=2) .put_seed(0, prefix="wt") .put_seed(0, prefix="wt", suffix=2) .put_password(client.encodedpass) .put_username(client.name) .finish() ) options = { "level_name": name, "level_desc": desc, "level_version": version, "level_length": length.value, "audio_track": audio_track, "auto": int(is_auto), "original": int(original), "two_player": int(two_player), "objects": objects, "coins": coins, "requested_stars": stars, "unlisted": int(unlisted), "ldm": int(ldm), "password": pwd, "level_string": data, "extra_string": extra_string, "level_info": "H4sIAAAAAAAAC_NIrVQoyUgtStVRCMpPSi0qUbDStwYAsgpl1RUAAAA=", } payload_cased = { Converter.snake_to_camel(key): str(value) for key, value in options.items() } payload.update(payload_cased) level_id = await self.http.request(Route.UPLOAD_LEVEL, payload) if level_id == -1: raise MissingAccess("Failed to upload a level.") return level_id
def _ord_index(self) -> Optional[str]: if self.index is not None: return Converter.to_ordinal(self.index)
def _ord_index(self) -> str: if self.index is not None: return Converter.to_ordinal(self.index)