示例#1
0
    def from_content(cls, content):
        """
        Gets the boosted creature from any Tibia.com page.


        Parameters
        ----------
        content: :class:`str`
            The HTML content of a Tibia.com page.

        Returns
        -------
        :class:`News`
            The boosted article shown.

        Raises
        ------
        InvalidContent
            If content is not the HTML of a Tibia.com's page.
        """
        try:
            parsed_content = bs4.BeautifulSoup(content.replace('ISO-8859-1', 'utf-8'), "lxml",
                                               parse_only=bs4.SoupStrainer("div", attrs={"id": "RightArtwork"}))
            img = parsed_content.find("img", attrs={"id": "Monster"})
            name = img["title"].replace(BOOSTED_ALT, "").strip()
            image_url = img["src"]
            return cls(name, image_url)
        except TypeError:
            raise InvalidContent("content is not from Tibia.com")
示例#2
0
    def from_content(cls, content):
        """Parses the content of the World Overview section from Tibia.com into an object of this class.

        Parameters
        ----------
        content: :class:`str`
            The HTML content of the World Overview page in Tibia.com

        Returns
        -------
        :class:`WorldOverview`
            An instance of this class containing all the information.

        Raises
        ------
        InvalidContent
            If the provided content is not the HTML content of the worlds section in Tibia.com
        """
        parsed_content = parse_tibiacom_content(
            content, html_class="TableContentAndRightShadow")
        world_overview = WorldOverview()
        try:
            record_row, *rows = parsed_content.find_all("tr")
            m = record_regexp.search(record_row.text)
            world_overview.record_count = parse_integer(m.group("count"))
            world_overview.record_date = parse_tibia_datetime(m.group("date"))
            world_rows = rows
            world_overview._parse_worlds(world_rows)
            return world_overview
        except (AttributeError, KeyError, ValueError):
            raise InvalidContent(
                "content does not belong to the World Overview section in Tibia.com"
            )
示例#3
0
    def list_from_content(cls, content):
        """
        Gets a list of news from the HTML content of the news search page.

        Parameters
        ----------
        content: :class:`str`
            The HTML content of the page.

        Returns
        -------
        :class:`list` of :class:`ListedNews`
            List of news in the search results.

        Raises
        ------
        InvalidContent
            If content is not the HTML of a news search's page.
        """
        try:
            parsed_content = parse_tibiacom_content(content)
            tables = parsed_content.find_all("table", attrs={"width": "100%"})
            news = []
            news_table = tables[0]
            title_row = news_table.find("td",
                                        attrs={
                                            "class": "white",
                                            "colspan": "3"
                                        })
            if title_row.text != "Search Results":
                raise InvalidContent(
                    "content is not from the news archive section in Tibia.com"
                )
            rows = news_table.find_all("tr", attrs={"class": ["Odd", "Even"]})
            for row in rows:
                cols_raw = row.find_all('td')
                if len(cols_raw) != 3:
                    continue
                entry = cls._parse_entry(cols_raw)
                news.append(entry)
            return news
        except (AttributeError, IndexError):
            raise InvalidContent(
                "content is not from the news archive section in Tibia.com")
示例#4
0
    def from_content(cls, content):
        """Creates an instance of the class from the HTML content of the kill statistics' page.

        Parameters
        -----------
        content: :class:`str`
            The HTML content of the page.

        Returns
        ----------
        :class:`KillStatistics`
            The kill statistics contained in the page or None if it doesn't exist.

        Raises
        ------
        InvalidContent
            If content is not the HTML of a kill statistics' page.
        """
        try:
            parsed_content = parse_tibiacom_content(content)
            selection_table = parsed_content.find(
                'div', attrs={'class': 'TableContainer'})
            world = selection_table.find("option", {"selected": True})["value"]

            entries_table = parsed_content.find('table',
                                                attrs={
                                                    'border': '0',
                                                    'cellpadding': '3'
                                                })
            # If the entries table doesn't exist, it means that this belongs to an nonexistent or unselected world.
            if entries_table is None:
                return None
            header, subheader, *rows = entries_table.find_all('tr')
            entries = {}
            total = None
            for i, row in enumerate(rows):
                columns_raw = row.find_all('td')
                columns = [
                    c.text.replace('\xa0', ' ').strip() for c in columns_raw
                ]
                entry = RaceEntry(
                    last_day_players_killed=int(columns[1]),
                    last_day_killed=int(columns[2]),
                    last_week_players_killed=int(columns[3]),
                    last_week_killed=int(columns[4]),
                )
                if i == len(rows) - 1:
                    total = entry
                else:
                    entries[columns[0]] = entry
            return cls(world, entries, total)
        except AttributeError:
            raise InvalidContent(
                "content does not belong to a Tibia.com kill statistics page.")
示例#5
0
    def from_tibiadata(cls, content, vocation=None):
        """Builds a highscores object from a TibiaData highscores response.

        Notes
        -----
        Since TibiaData.com's response doesn't contain any indication of the vocation filter applied,
        :py:attr:`vocation` can't be determined from the response, so the attribute must be assigned manually.

        If the attribute is known, it can be passed for it to be assigned in this method.

        Parameters
        ----------
        content: :class:`str`
            The JSON content of the response.
        vocation: :class:`VocationFilter`, optional
            The vocation filter to assign to the results. Note that this won't affect the parsing.

        Returns
        -------
        :class:`Highscores`
            The highscores contained in the page, or None if the content is for the highscores of a nonexistent world.

        Raises
        ------
        InvalidContent
            If content is not a JSON string of the highscores response."""
        json_content = parse_json(content)
        try:
            highscores_json = json_content["highscores"]
            if "error" in highscores_json["data"]:
                return None
            world = highscores_json["world"]
            category = highscores_json["type"]
            highscores = cls(world, category)
            for entry in highscores_json["data"]:
                value_key = "level"
                if highscores.category in [Category.ACHIEVEMENTS, Category.LOYALTY_POINTS, Category.EXPERIENCE]:
                    value_key = "points"
                if highscores.category == Category.EXPERIENCE:
                    highscores.entries.append(ExpHighscoresEntry(entry["name"], entry["rank"], entry["voc"],
                                                                 entry[value_key], entry["level"]))
                elif highscores.category == Category.LOYALTY_POINTS:
                    highscores.entries.append(LoyaltyHighscoresEntry(entry["name"], entry["rank"], entry["voc"],
                                                                     entry[value_key], entry["title"]))
                else:
                    highscores.entries.append(HighscoresEntry(entry["name"], entry["rank"], entry["voc"],
                                                              entry[value_key]))
            highscores.results_count = len(highscores.entries)
        except KeyError:
            raise InvalidContent("content is not a TibiaData highscores response.")
        highscores.vocation = vocation or VocationFilter.ALL
        return highscores
示例#6
0
    def from_tibiadata(cls, content):
        """Parses a TibiaData.com response into a :class:`World`

        Parameters
        ----------
        content: :class:`str`
            The raw JSON content from TibiaData

        Returns
        -------
        :class:`World`
            The World described in the page, or ``None``.

        Raises
        ------
        InvalidContent
            If the provided content is not a TibiaData world response.
        """
        json_data = parse_json(content)
        try:
            world_data = json_data["world"]
            world_info = world_data["world_information"]
            world = cls(world_info["name"])
            if "location" not in world_info:
                return None
            world.online_count = world_info["players_online"]
            world.status = "Online" if world.online_count > 0 else "Offline"
            world.record_count = world_info["online_record"]["players"]
            world.record_date = parse_tibiadata_datetime(
                world_info["online_record"]["date"])
            world.creation_date = world_info["creation_date"]
            world.location = try_enum(WorldLocation, world_info["location"])
            world.pvp_type = try_enum(PvpType, world_info["pvp_type"])
            world.transfer_type = try_enum(TransferType,
                                           world_info.get("transfer_type"),
                                           TransferType.REGULAR)
            world.premium_only = "premium_type" in world_info
            world.world_quest_titles = world_info.get("world_quest_titles", [])
            world._parse_battleye_status(world_info.get("battleye_status", ""))
            world.experimental = world_info.get("Game World Type:",
                                                "Regular") != "Regular"
            for player in world_data.get("players_online", []):
                world.online_players.append(
                    OnlineCharacter(player["name"], world.name,
                                    player["level"], player["vocation"]))
            return world
        except KeyError:
            raise InvalidContent(
                "content is not a world json response from TibiaData")
示例#7
0
    def from_content(cls, content):
        """Creates an instance of the class from the html content of a highscores page.

        Notes
        -----
        Tibia.com only shows up to 25 entries per page, so in order to obtain the full highscores, all 12 pages must
        be parsed and merged into one.

        Parameters
        ----------
        content: :class:`str`
            The HTML content of the page.

        Returns
        -------
        :class:`Highscores`
            The highscores results contained in the page.

        Raises
        ------
        InvalidContent
            If content is not the HTML of a highscore's page."""
        parsed_content = parse_tibiacom_content(content)
        tables = cls._parse_tables(parsed_content)
        filters = tables.get("Highscores Filter")
        if filters is None:
            raise InvalidContent("content does is not from the highscores section of Tibia.com")
        world_filter, vocation_filter, category_filter = filters
        world = world_filter.find("option", {"selected": True})["value"]
        if world == "":
            return None
        category = category_filter.find("option", {"selected": True})["value"]
        vocation_selected = vocation_filter.find("option", {"selected": True})
        vocation = int(vocation_selected["value"]) if vocation_selected else 0
        highscores = cls(world, category, vocation=vocation)
        entries = tables.get("Highscores")
        if entries is None:
            return None
        _, header, *rows = entries
        info_row = rows.pop()
        highscores.results_count = int(results_pattern.search(info_row.text).group(1))
        for row in rows:
            cols_raw = row.find_all('td')
            if "There is currently no data" in cols_raw[0].text:
                break
            highscores._parse_entry(cols_raw)
        return highscores
示例#8
0
    def from_tibiadata(cls, content):
        """Parses the content of the World Overview section from TibiaData.com into an object of this class.

        Notes
        -----
        Due to TibiaData limitations, :py:attr:`record_count` and :py:attr:`record_date` are unavailable
        object.

        Additionally, the listed worlds in :py:attr:`worlds` lack some information when obtained from TibiaData.
        The following attributes are unavailable:

        - :py:attr:`ListedWorld.status` is always ``Online``.
        - :py:attr:`ListedWorld.battleye_protected` is always ``False``
        - :py:attr:`ListedWorld.battleye_date` is always ``None``.


        Parameters
        ----------
        content: :class:`str`
            The JSON response of the worlds section in TibiaData.com

        Returns
        -------
        :class:`WorldOverview`
            An instance of this class containing only the available worlds.

        Raises
        ------
        InvalidContent
            If the provided content is the json content of the world section in TibiaData.com
        """
        json_data = parse_json(content)
        try:
            worlds_json = json_data["worlds"]["allworlds"]
            world_overview = cls()
            for world_json in worlds_json:
                world = ListedWorld(world_json["name"], world_json["location"],
                                    world_json["worldtype"])
                world._parse_additional_info(world_json["additional"])
                world.online_count = world_json["online"]
                world_overview.worlds.append(world)
            return world_overview
        except KeyError:
            raise InvalidContent(
                "content is not a worlds json response from TibiaData.com.")
示例#9
0
    def from_content(cls, content):
        """Parses a Tibia.com response into a :class:`World`.

        Parameters
        ----------
        content: :class:`str`
            The raw HTML from the server's information page.

        Returns
        -------
        :class:`World`
            The World described in the page, or ``None``.

        Raises
        ------
        InvalidContent
            If the provided content is not the html content of the world section in Tibia.com
        """
        parsed_content = parse_tibiacom_content(content)
        tables = cls._parse_tables(parsed_content)
        try:
            error = tables.get("Error")
            if error and error[0].text == "World with this name doesn't exist!":
                return None
            selected_world = parsed_content.find('option', selected=True)
            world = cls(selected_world.text)
            world._parse_world_info(tables.get("World Information", []))

            online_table = tables.get("Players Online", [])
            world.online_players = []
            for row in online_table[1:]:
                cols_raw = row.find_all('td')
                name, level, vocation = (c.text.replace('\xa0', ' ').strip()
                                         for c in cols_raw)
                world.online_players.append(
                    OnlineCharacter(name, world.name, int(level), vocation))
        except AttributeError:
            raise InvalidContent(
                "content is not from the world section in Tibia.com")

        return world
示例#10
0
    def from_content(cls, content, news_id=0):
        """
        Gets a news entry by its HTML content from Tibia.com

        Notes
        -----
        Since there's no way to obtain the entry's Id from the page contents, it will always be 0.
        A news_id can be passed to set the news_id of the resulting object.

        Parameters
        ----------
        content: :class:`str`
            The HTML content of the page.
        news_id: :class:`int`, optional
            The news_id belonging to the content being parsed.

        Returns
        -------
        :class:`News`
            The news article found in the page.

        Raises
        ------
        InvalidContent
            If content is not the HTML of a news' page.
        """
        if "(no news with id " in content:
            return None
        try:
            parsed_content = parse_tibiacom_content(content)
            # Read Information from the headline
            headline = parsed_content.find("div",
                                           attrs={"class": "NewsHeadline"})
            img = headline.find('img')
            img_url = img["src"]
            category_name = ICON_PATTERN.search(img_url)
            category = try_enum(NewsCategory, category_name.group(1))
            title_div = headline.find("div",
                                      attrs={"class": "NewsHeadlineText"})
            title = title_div.text.replace('\xa0', ' ')
            date_div = headline.find("div",
                                     attrs={"class": "NewsHeadlineDate"})
            date_str = date_div.text.replace('\xa0', ' ').replace('-',
                                                                  '').strip()
            date = parse_tibia_date(date_str)

            # Read the page's content.
            content_table = parsed_content.find("table")
            content_row = content_table.find("td")
            content = content_row.encode_contents().decode()
            thread_id = None
            thread_div = content_table.find("div")
            if thread_div:
                news_link = thread_div.find('a')
                url = urllib.parse.urlparse(news_link["href"])
                query = urllib.parse.parse_qs(url.query)
                thread_id = int(query["threadid"][0])

            return cls(news_id,
                       title,
                       content,
                       date,
                       category,
                       thread_id=thread_id,
                       category_icon=img_url)
        except AttributeError:
            raise InvalidContent(
                "content is not from the news archive section in Tibia.com")