Esempio n. 1
0
    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)
Esempio n. 2
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,
        )
Esempio n. 3
0
 def from_data(cls,
               data: ExtDict,
               type: str = "normal",
               *,
               client: Client) -> AbstractUser:
     return cls(
         account_id=data.getcast(Index.USER_ACCOUNT_ID, 0, int),
         id=data.getcast(Index.USER_PLAYER_ID, 0, int),
         name=data.get(Index.USER_NAME, "unknown"),
         client=client,
     )
Esempio n. 4
0
 def from_data(cls, data: ExtDict, client: Client) -> UserStats:
     return cls(
         account_id=data.getcast(Index.USER_ACCOUNT_ID, 0, int),
         name=data.get(Index.USER_NAME, "unknown"),
         id=data.getcast(Index.USER_PLAYER_ID, 0, int),
         stars=data.getcast(Index.USER_STARS, 0, int),
         demons=data.getcast(Index.USER_DEMONS, 0, int),
         cp=data.getcast(Index.USER_CREATOR_POINTS, 0, int),
         diamonds=data.getcast(Index.USER_DIAMONDS, 0, int),
         coins=data.getcast(Index.USER_COINS, 0, int),
         secret_coins=data.getcast(Index.USER_SECRET_COINS, 0, int),
         place=data.getcast(Index.USER_TOP_PLACE, 0, int),
         client=client,
     )
Esempio n. 5
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,
        )
Esempio n. 6
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,
        )
Esempio n. 7
0
    async def test_song(self, song_id: int = 0) -> ExtDict:
        codes = {-1: MissingAccess(f"Failed to fetch artist info for ID: {song_id}")}
        payload = Params().create_new("web").put_definer("song", song_id).close()
        resp = await self.http.request(
            Route.TEST_SONG, params=payload, method="get", error_codes=codes
        )

        data = ExtDict(id=song_id)

        try:
            data.update(extract_info_from_endpoint(resp))
        except ValueError:
            raise MissingAccess(f"Failed to load data. Response: {resp!r}.") from None

        return data
Esempio n. 8
0
    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
Esempio n. 9
0
    def to_normal_song(song_id: int, server_style: bool = True) -> ExtDict:
        if server_style:
            cases = _cases
        else:
            cases = {number + 1: value for number, value in _cases.items()}

        # get author and name, just like gd does
        author, name = cases.get(song_id, ("DJVI", "Unknown"))
        return ExtDict(name=name, author=author, id=song_id, size=0.0, links={}, custom=False)
Esempio n. 10
0
def extract_info_from_endpoint(text: str) -> ExtDict:
    artist, whitelisted, scouted, song, api, *_ = filter(
        is_not_empty, re.split(r"</?br>", text))
    return ExtDict(
        artist=artist.split("Artist: ").pop(),
        song=song.split("Song: ").pop(),
        whitelisted=check_not(whitelisted),
        scouted=check_not(scouted),
        api=check_not(api),
    )
Esempio n. 11
0
def extract_users(text: str) -> List[ExtDict]:
    tree, result = html_parse(text), []

    for a in tree.findall(r'.//div[@class="item-details-main"]/h4/a'):
        url = URL(a.attrib["href"]).with_scheme("https")
        name = a.text

        result.append(ExtDict(link=url, name=name))

    return result
Esempio n. 12
0
    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
Esempio n. 13
0
 def from_data(cls, data: ExtDict, strategy: LevelLeaderboardStrategy,
               client: Client) -> LevelRecord:
     return cls(
         account_id=data.getcast(Index.USER_ACCOUNT_ID, 0, int),
         name=data.get(Index.USER_NAME, "unknown"),
         id=data.getcast(Index.USER_PLAYER_ID, 0, int),
         level_id=data.getcast(Index.USER_LEVEL_ID, 0, int),
         place=data.getcast(Index.USER_TOP_PLACE, 0, int),
         percentage=data.getcast(Index.USER_PERCENT, 0, int),
         coins=data.getcast(Index.USER_SECRET_COINS, 0, int),
         timestamp=data.get(Index.USER_RECORD_TIMESTAMP, "unknown"),
         type=strategy,
         client=client,
     )
Esempio n. 14
0
 def from_data(cls, data: ExtDict, *, custom: bool = True, client: Client) -> Song:
     return cls(
         # name and author - cp1252 encoding seems to fix weird characters - Alex1304
         name=fix_song_encoding(data.get(Index.SONG_TITLE, "unknown")),
         author=fix_song_encoding(data.get(Index.SONG_AUTHOR, "unknown")),
         id=data.getcast(Index.SONG_ID, 0, int),
         size=data.getcast(Index.SONG_SIZE, 0.0, float),
         links=dict(
             normal=Route.NEWGROUNDS_SONG_LISTEN + data.get(Index.SONG_ID, ""),
             download=unquote(data.get(Index.SONG_URL, "")),
         ),
         custom=custom,
         client=client,
     )
Esempio n. 15
0
    async def get_ng_song(self, song_id: int = 0) -> ExtDict:
        # just like get_song(), but gets anything available on NG.
        link = Route.NEWGROUNDS_SONG_LISTEN + str(song_id)

        content = await self.http.normal_request(link)
        html = content.decode().replace("\\", "")

        try:
            info = find_song_info(html)
        except ValueError:
            raise MissingAccess(f"Song was not found by ID: {song_id}") from None

        return ExtDict(
            name=info.name,
            author=info.author,
            id=song_id,
            size=round(info.size / 1024 / 1024, 2),
            links=dict(normal=link, download=info.link),
            custom=True,
        )
Esempio n. 16
0
def extract_user_songs(
    json: Dict[str, Dict[str, Dict[str, Union[Dict[str, str], List[str]]]]]
) -> List[ExtDict]:
    result = []

    try:
        years = json["years"].values()
    except (TypeError, AttributeError):  # not found
        return result

    for entry in chain.from_iterable(year["items"] for year in years):
        tree = html_parse(entry)
        a = tree.findall(r'.//a[@class="item-link"]')[0]

        url = URL(a.attrib["href"]).with_scheme("https")
        song_id = int(url.parts[-1])

        name = a.attrib["title"]

        result.append(
            ExtDict(id=song_id, name=name, links={"normal": str(url)}))

    return result
Esempio n. 17
0
def search_song_data(text: str) -> List[ExtDict]:
    tree, result = html_parse(text), []

    for a, div in zip(
            tree.findall(r'.//a[@class="item-audiosubmission"]'),
            tree.findall(r'.//div[@class="detail-title"]'),
    ):
        url = URL(a.attrib["href"]).with_scheme("https")
        song_id = int(url.parts[-1])

        h4, span, *_ = div.getchildren()

        name = switch_if_none(h4.text, "") + "".join(
            switch_if_none(mark.text, "") + switch_if_none(mark.tail, "")
            for mark in h4.getchildren())
        author = span.getchildren()[0].text

        result.append(
            ExtDict(id=song_id,
                    name=name,
                    author=author,
                    links={"normal": str(url)}))

    return result
Esempio n. 18
0
    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,
        )
Esempio n. 19
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,
        )
Esempio n. 20
0
    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
Esempio n. 21
0
 def parse_dict(self) -> ExtDict:
     return ExtDict(
         {k: getattr(self, k)
          for k in ("name", "id", "account_id")})
Esempio n. 22
0
    def from_data(cls, data: ExtDict, client: Client) -> User:
        youtube = data.get(Index.USER_YOUTUBE, "")
        youtube = {
            "normal": youtube,
            "link": "https://www.youtube.com/channel/" + youtube
        }
        twitter = data.get(Index.USER_TWITTER, "")
        twitter = {"normal": twitter, "link": "https://twitter.com/" + twitter}
        twitch = data.get(Index.USER_TWITCH, "")
        twitch = {"normal": twitch, "link": "https://twitch.tv/" + twitch}

        return cls(
            name=data.get(Index.USER_NAME, "unknown"),
            id=data.getcast(Index.USER_PLAYER_ID, 0, int),
            stars=data.getcast(Index.USER_STARS, 0, int),
            demons=data.getcast(Index.USER_DEMONS, 0, int),
            secret_coins=data.getcast(Index.USER_SECRET_COINS, 0, int),
            coins=data.getcast(Index.USER_COINS, 0, int),
            cp=data.getcast(Index.USER_CREATOR_POINTS, 0, int),
            diamonds=data.getcast(Index.USER_DIAMONDS, 0, int),
            role=data.getcast(Index.USER_ROLE, 0, int),
            global_rank=data.getcast(Index.USER_GLOBAL_RANK, None, int),
            account_id=data.getcast(Index.USER_ACCOUNT_ID, 0, int),
            youtube=youtube,
            twitter=twitter,
            twitch=twitch,
            message_policy=MessagePolicyType.from_value(
                data.getcast(Index.USER_PRIVATE_MESSAGE_POLICY, 0, int), 0),
            friend_request_policy=FriendRequestPolicyType.from_value(
                data.getcast(Index.USER_FRIEND_REQUEST_POLICY, 0, int), 0),
            comment_policy=CommentPolicyType.from_value(
                data.getcast(Index.USER_COMMENT_HISTORY_POLICY, 0, int), 0),
            icon_setup=IconSet(
                main_icon=data.getcast(Index.USER_ICON, 1, int),
                color_1=colors[data.getcast(Index.USER_COLOR_1, 0, int)],
                color_2=colors[data.getcast(Index.USER_COLOR_2, 0, int)],
                main_icon_type=IconType.from_value(
                    data.getcast(Index.USER_ICON_TYPE, 0, int), 0),
                has_glow_outline=bool(
                    data.getcast(Index.USER_GLOW_OUTLINE_2, 0, int)),
                icon_cube=data.getcast(Index.USER_ICON_CUBE, 1, int),
                icon_ship=data.getcast(Index.USER_ICON_SHIP, 1, int),
                icon_ball=data.getcast(Index.USER_ICON_BALL, 1, int),
                icon_ufo=data.getcast(Index.USER_ICON_UFO, 1, int),
                icon_wave=data.getcast(Index.USER_ICON_WAVE, 1, int),
                icon_robot=data.getcast(Index.USER_ICON_ROBOT, 1, int),
                icon_spider=data.getcast(Index.USER_ICON_SPIDER, 1, int),
                icon_explosion=data.getcast(Index.USER_EXPLOSION, 1, int),
                client=client,
            ),
            client=client,
        )