from enum import Enum
from datetime import datetime
from contextlib import suppress
import math
import asyncio
import json
import aiohttp
from ..gametypes import GameType
RAW_ACHIEVEMENTS_URL = "https://api.hypixel.net/resources/achievements"
__all__ = [
"OneTimeAchievement", "TieredAchievement", "AchievementTier",
"PixelAchievements", "PlayerAchievements", "PlayerRank", "Player"
]
[docs]class OneTimeAchievement:
def __init__(self, data: tuple):
self.game = data[0]
self.db_name = data[1]
self.points = data[2]["points"]
self.name = data[2]["name"]
self.description = data[2]["description"]
def __repr__(self):
return "<OneTimeAchievement game={0.game} db_name={0.db_name} "\
"name={0.name} points={0.points} description={0.description}"\
"".format(self)
[docs]class TieredAchievement:
def __init__(self, data: tuple):
self.game = data[0]
self.db_name = data[1]
self.player_value = data[3]
self.name = data[2]["name"]
self.description = data[2]["description"]
completed_tiers = []
unfinished_tiers = []
for tier in data[2]["tiers"]:
if tier["amount"] <= self.player_value:
completed_tiers.append(AchievementTier(**tier))
else:
unfinished_tiers.append(AchievementTier(**tier))
self.completed_tiers = completed_tiers
self.unfinished_tiers = unfinished_tiers
[docs]class AchievementTier:
def __init__(self, tier: int, points: int, amount: int):
self.tier = tier
self.points = points
self.amount = amount
[docs]class PixelAchievements:
achievements = None
def __init__(self):
loop = asyncio.get_event_loop()
loop.create_task(self.get_all_achievements())
self.achievements = None
[docs] async def get_all_achievements(self):
async with aiohttp.ClientSession() as session:
async with session.get(RAW_ACHIEVEMENTS_URL) as r:
if r.status == 200:
try:
data = json.loads(await r.text())
except aiohttp.ClientResponseError as e:
pass
else:
self.achievements = data["achievements"]
[docs] def get_one_time_achievement(self, name: str):
game, achievement_name = name.split("_", 1)
if game not in self.achievements:
return None
if not achievement_name.upper() in self.achievements[game]["one_time"]:
return None
raw_achievement = \
self.achievements[game]["one_time"][achievement_name.upper()]
return OneTimeAchievement((game, achievement_name, raw_achievement))
[docs] def get_tiered_achievement(self, name: str, player_value: int):
game, achievement_name = name.split("_", 1)
if game not in self.achievements:
return None
if not achievement_name.upper() in self.achievements[game]["tiered"]:
return None
raw_achv = self.achievements[game]["tiered"][achievement_name.upper()]
return TieredAchievement(
(game, achievement_name, raw_achv, player_value)
)
[docs]class PlayerAchievements:
def __init__(self, data, all_achievements: PixelAchievements):
self.all_achievements = all_achievements
one_time = []
tiered = []
if "achievementsOneTime" in data:
for item in data["achievementsOneTime"]:
one_time.append(
self.all_achievements.get_one_time_achievement(item)
)
if "achievements" in data:
for k, v in data["achievements"].items():
tiered.append(
self.all_achievements.get_tiered_achievement(k, v)
)
self.one_time = one_time
self.tiered = tiered
[docs]class PlayerRank(Enum):
"""
An enum representing player ranks
"""
ADMIN = "Admin"
MODERATOR = "Moderator"
HELPER = "Helper"
JR_HELPER = "Jr. Helper"
YOUTUBER = "Youtuber"
BUILD_TEAM = "Build Team"
SUPERSTAR = "MVP++"
MVP_PLUS = "MVP+"
MVP = "MVP"
VIP_PLUS = "VIP+"
VIP = "VIP"
NONE = ""
def __init__(self, pretty_name: str):
self.pretty_name = pretty_name
[docs] @classmethod
def from_player_data(cls, data: dict):
"""
Gets the player's rank from their player data
"""
if "rank" in data:
return getattr(cls, data["rank"])
elif data.get("buildTeam") is True:
return cls.BUILD_TEAM
elif data.get("monthlyPackageRank") == "SUPERSTAR":
return cls.SUPERSTAR
elif "newPackageRank" in data:
return getattr(cls, data["newPackageRank"])
elif "packageRank" in data:
return getattr(cls, data["packageRank"])
else:
return cls.NONE
[docs]class Player:
def __init__(self, _id: str, displayname: str, first_login: int,
last_login: int, karma: int, network_exp: float,
rank: PlayerRank, last_logout: int,
achievements: PlayerAchievements, stats: list,
most_recent_game_type: GameType):
self._id = _id
self.displayname = displayname
self.first_login = datetime.utcfromtimestamp(first_login/1000)
self.last_login = datetime.utcfromtimestamp(last_login/1000)
self.karma = karma
self.network_exp = network_exp
self.rank = rank
self.last_logout = datetime.utcfromtimestamp(last_logout/1000)
self.achievements = achievements
self.stats = stats
self.most_recent_game_type = most_recent_game_type
[docs] def network_level(self) -> float:
"""
Calculate the player's network level
Returns
-------
float
The player's network level
"""
base = 10000
growth = 2500
reverse_pq_prefix = -(base - 0.5 * growth) / growth
reverse_const = math.pow(reverse_pq_prefix, 2)
growth_divides_2 = 2 / growth
return 1.0 if self.network_exp < 0 \
else 1 + reverse_pq_prefix + math.sqrt(
reverse_const + growth_divides_2 * self.network_exp
)
[docs] def online(self) -> bool:
"""
Show whether the player is online or not
"""
return self.last_logout < self.last_login