示例#1
0
    async def like(
        self, item_id: int, typeid: int, special: int, dislike: bool = False, *, client: Client
    ) -> None:
        like = dislike ^ 1

        rs, udid, uuid = Coder.gen_rs(), Params.gen_udid(), Params.gen_uuid()
        values = [special, item_id, like, typeid, rs, client.account_id, udid, uuid]
        chk = Coder.gen_chk(type="like_rate", values=values)

        payload = (
            Params()
            .create_new(game_version=20)
            .put_definer("accountid", client.account_id)
            .put_password(client.encodedpass)
            .put_udid(udid)
            .put_uuid(uuid)
            .put_definer("itemid", item_id)
            .put_like(like)
            .put_type(typeid)
            .put_special(special)
            .put_rs(rs)
            .put_chk(chk)
            .finish()
        )

        resp = await self.http.request(Route.LIKE_ITEM, payload)

        if resp != 1:
            raise MissingAccess(f"Failed to like an item by ID: {item_id}.")
示例#2
0
    async def rate_level(self, level_id: int, rating: int, *, client: Client) -> None:
        assert 0 < rating <= 10, "Invalid star value given."

        rs, udid, uuid = Coder.gen_rs(), Params.gen_udid(), Params.gen_uuid()
        values = [level_id, rating, rs, client.account_id, udid, uuid]
        chk = Coder.gen_chk(type="like_rate", values=values)

        payload = (
            Params()
            .create_new()
            .put_definer("levelid", level_id)
            .put_definer("accountid", client.account_id)
            .put_password(client.encodedpass)
            .put_udid(udid)
            .put_uuid(uuid)
            .put_definer("stars", rating)
            .put_rs(rs)
            .put_chk(chk)
            .finish()
        )

        resp = await self.http.request(Route.RATE_LEVEL_STARS, payload)

        if resp != 1:
            raise MissingAccess(f"Failed to rate level by ID: {level_id}.")
示例#3
0
def _attempt_zip(s: str):
    unzip = all(char not in s for char in "|;,.")  # O(4n)
    try:
        if unzip:
            return Coder.unzip(s)
        return Coder.zip(s)
    except Exception:
        return s
示例#4
0
def _attempt_zip(string: str) -> str:
    unzip = all(char not in string for char in "|;,.")  # O(m*n)

    try:
        if unzip:
            return Coder.unzip(string)

        return Coder.zip(string)

    except Exception:
        return string
示例#5
0
    def from_data(cls, data: ExtDict, user_2: Union[ExtDict, AbstractUser],
                  client: Client) -> FriendRequest:
        user_1 = AbstractUser(
            name=data.get(Index.REQUEST_SENDER_NAME, "unknown"),
            id=data.getcast(Index.REQUEST_SENDER_ID, 0, int),
            account_id=data.getcast(Index.REQUEST_SENDER_ACCOUNT_ID, 0, int),
            client=client,
        )
        if isinstance(user_2, ExtDict):
            user_2 = AbstractUser(**user_2, client=client)

        indicator = data.getcast(Index.REQUEST_INDICATOR, 0, int)
        is_normal = indicator ^ 1

        return cls(
            id=data.getcast(Index.REQUEST_ID, 0, int),
            timestamp=str(data.get(Index.REQUEST_TIMESTAMP, "unknown")),
            body=Coder.do_base64(data.get(Index.REQUEST_BODY, ""),
                                 encode=False,
                                 errors="replace"),
            is_read=(not data.get(Index.REQUEST_STATUS)),
            author=(user_1 if is_normal else user_2),
            recipient=(user_2 if is_normal else user_1),
            type=MessageOrRequestType.from_value(indicator, 0),
            client=client,
        )
示例#6
0
    def from_data(cls, data: ExtDict, user_2: Union[ExtDict, AbstractUser],
                  client: Client) -> Message:
        user_1 = AbstractUser(
            name=data.get(Index.MESSAGE_SENDER_NAME, "unknown"),
            id=data.getcast(Index.MESSAGE_SENDER_ID, 0, int),
            account_id=data.getcast(Index.MESSAGE_SENDER_ACCOUNT_ID, 0, int),
            client=client,
        )
        if isinstance(user_2, ExtDict):
            user_2 = AbstractUser(**user_2, client=client)

        indicator = data.getcast(Index.MESSAGE_INDICATOR, 0, int)
        is_normal = indicator ^ 1

        subject = Coder.do_base64(data.get(Index.MESSAGE_SUBJECT, ""),
                                  encode=False,
                                  errors="replace")

        return Message(
            id=data.getcast(Index.MESSAGE_ID, 0, int),
            timestamp=data.get(Index.MESSAGE_TIMESTAMP, "unknown"),
            subject=subject,
            is_read=bool(data.getcast(Index.MESSAGE_IS_READ, 0, int)),
            author=(user_1 if is_normal else user_2),
            recipient=(user_2 if is_normal else user_1),
            type=MessageOrRequestType.from_value(indicator, 0),
            client=client,
        )
示例#7
0
    def put_save_data(self, data_seq: Sequence[Union[bytes,
                                                     str]]) -> Parameters:
        """Self explanatory. Puts `'saveData'` parameter.

        Parameters
        ----------
        data_seq: Sequence[Union[:class:`bytes`, :class:`str`]]
            Data to put.

        Returns
        -------
        :class:`.Parameters`
            ``self``
        """
        parts = []

        for data in data_seq:
            if isinstance(data, bytes):
                data = data.decode(errors="ignore")

            if "?xml" in data:  # not encoded
                data = Coder.encode_save(data, needs_xor=False)

            parts.append(data)

        self.dict["saveData"] = ";".join(parts)

        return self
示例#8
0
    def from_data(cls, data: ExtDict, author: Union[ExtDict, AbstractUser],
                  client: Client) -> Comment:
        if isinstance(author, ExtDict):
            if any(key.isdigit() for key in author.keys()):
                author = AbstractUser.from_data(author, client=client)
            else:
                author = AbstractUser(**author, client=client)

        color_string = data.get(Index.COMMENT_COLOR, "255,255,255")
        color = Color.from_rgb(*map(int, color_string.split(",")))

        return cls(
            body=Coder.do_base64(data.get(Index.COMMENT_BODY, ""),
                                 encode=False,
                                 errors="replace"),
            rating=data.getcast(Index.COMMENT_RATING, 0, int),
            timestamp=data.get(Index.COMMENT_TIMESTAMP, "unknown"),
            id=data.getcast(Index.COMMENT_ID, 0, int),
            is_spam=bool(data.getcast(Index.COMMENT_IS_SPAM, 0, int)),
            type=CommentType.from_value(
                data.getcast(Index.COMMENT_TYPE, 0, int), 0),
            color=color,
            level_id=data.getcast(Index.COMMENT_LEVEL_ID, 0, int),
            level_percentage=data.getcast(Index.COMMENT_LEVEL_PERCENTAGE, -1,
                                          int),
            author=author,
            client=client,
        )
示例#9
0
    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}")
示例#10
0
    def put_message(self, subject: str, body: str) -> Parameters:
        """Puts message's subject and body.

        Parameters
        ----------
        subject: :class:`str`
            NOT ENCODED message subject.
        body: :class:`str`
            NOT ENCODED message body.

        Returns
        -------
        :class:`.Parameters`
            ``self``
        """
        self.dict["subject"] = Coder.do_base64(subject)
        self.dict["body"] = Coder.encode(type="message", string=body)
        return self
示例#11
0
    def into_level(
        self,
        client: Optional[Client] = None,
        get_data: bool = True,
        server_style: bool = False,
    ) -> Level:
        if self.is_demon():
            difficulty = DemonDifficulty.from_name(self.difficulty)
        else:
            difficulty = LevelDifficulty.from_name(self.difficulty)

        if get_data:
            data = official_levels_data.get(self.name, "")

            if data:
                data = Coder.unzip(data)

        else:
            data = ""

        return Level(
            id=self.level_id,
            name=self.name,
            description=f"Official Level: {self.name}",
            version=1,
            creator=AbstractUser(name="RobTop",
                                 id=16,
                                 account_id=71,
                                 client=client),
            song=Song.official(self.get_song_id(server_style),
                               client=client,
                               server_style=server_style),
            data=data,
            password=None,
            copyable=False,
            is_demon=self.is_demon(),
            is_auto=self.is_auto(),
            difficulty=difficulty,
            stars=self.stars,
            coins=self.coins,
            verified_coins=True,
            is_epic=False,
            original=True,
            low_detail_mode=False,
            downloads=0,
            rating=0,
            score=1,
            uploaded_timestamp="unknown",
            last_updated_timestamp="unknown",
            length=LevelLength.from_name(self.length),
            stars_requested=self.stars,
            object_count=0,
            type=TimelyType.from_value(0),
            time_n=-1,
            cooldown=-1,
            client=client,
        )
示例#12
0
    def put_comment(self, content: str, values: List[Any]) -> Parameters:
        """Puts a comment.

        Parameters
        ----------
        content: :class:`str`
            The content of the comment.

        values: :class:`list`
            A list of values to generate a ``chk`` parameter with.

        Returns
        -------
        :class:`.Parameters`
            ``self``
        """
        comment = Coder.do_base64(content)
        self.dict["comment"] = comment
        values.insert(1, comment)
        self.put_chk(Coder.gen_chk(type="comment", values=values))
        return self
示例#13
0
    def put_fr_comment(self, item: str) -> Parameters:
        """Pretty self explanatory. Puts `'comment'` parameter.

        Parameters
        ----------
        item: :class:`str`
            A comment to put.

        Returns
        -------
        :class:`.Parameters`
            ``self``
        """
        self.dict["comment"] = Coder.do_base64(item)
        return self
示例#14
0
    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, ""))
示例#15
0
    def put_level_desc(self, content: str) -> Parameters:
        """Encodes given content and puts ``'levelDesc'``.

        Parameters
        ----------
        content: :class:`str`
            Content of the new description, NOT encoded in Base64.

        Returns
        -------
        :class:`.Parameters`
            ``self``
        """
        if content is None:
            content = ""

        desc = Coder.do_base64(content)

        self.dict["levelDesc"] = desc
        return self
示例#16
0
    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,
        )
示例#17
0
def _b64_failsafe(string: str, encode: bool = True) -> str:
    try:
        return Coder.do_base64(string, encode=encode)
    except Exception:
        return string
示例#18
0
    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