PK ! dΠ aiohypixel/__init__.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
This is an asynchronous Hypixel API wrapper written in Python.
Compatible with Python3.6 and up
NOTE: It's highly recommended that you setup logging for this module! A lot of
info that you may not want to miss is reported through there. Doing it for
aiohttp might also be a good idea (INFO level is generaly enough).
"""
# TODO: Add documentation to all models' fields.
from . import shared, session, player, guild, keys, boosters, friends, watchdogstats, stats
from .session import *
from .shared import *
from .player import *
from .stats import *
from .guild import *
from .boosters import *
from .keys import *
from .friends import *
from .watchdogstats import *
from .leaderboards import *
from .resources import *
from .skyblock import *
__author__ = "Tmpod"
__url__ = f"https://gitlab.com/{__author__}/aiohypixel/"
__version__ = "0.2.0"
__licence__ = "LGPL-3.0"
__copyright__ = f"Copyright (c) 2018-2019 {__author__}"
PK ! 9- aiohypixel/__main__.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
This is just a little *crude* interface for the wrapper.
"""
from .session import HypixelSession
import logging, asyncio, sys
from shutil import get_terminal_size
WIN_SIZE = get_terminal_size()
def fmtp(s: str) -> str:
"""Just prints the string with a little delimiter around it"""
ndash = ("-" * (len(s) - 2)) if len(s) <= WIN_SIZE.columns else "-" * (WIN_SIZE.columns - 2)
print(f"\n<{ndash}>\n{s}\n<{ndash}>\n")
try:
API_KEY = sys.argv[1]
except IndexError:
fmtp("You must provide an API key!")
else:
try:
REQUEST_TYPE = sys.argv[2]
except IndexError:
fmtp("You must provide a get method!")
else:
logging.basicConfig(level="DEBUG")
try:
QUERY = sys.argv[3]
except IndexError:
QUERY = None
async def main(request_type: str, query: str = None) -> None:
session = await HypixelSession(API_KEY)
try:
if query is None:
fmtp(str(await (getattr(session, f"get_{request_type}"))()))
return
fmtp(str(await (getattr(session, f"get_{request_type}"))(query)))
except AttributeError:
fmtp("Invalid!")
asyncio.get_event_loop().run_until_complete(main(REQUEST_TYPE, QUERY))
PK ! aiohypixel/boosters.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Stuff for the boosters endpoint
"""
__all__ = ("Booster",)
from dataclasses import dataclass
from datetime import datetime
from typing import Union, Tuple
from .shared import APIResponse, GAME_TYPES_TABLE, HypixelModel
@dataclass(frozen=True)
class Booster(HypixelModel):
"""
Represents a
"""
purchaser_uuid: str
amount: int
original_length: int
current_length: int
game_type: int
game: str
activated_at: datetime
stacked: Union[bool, Tuple[str], None] # not sure what this is... I'll get back here soon
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Booster` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Booster` object.
"""
return cls(
purchaser_uuid=resp["purchaserUuid"],
amount=resp["amount"],
original_length=resp["originalLength"],
current_length=resp["length"],
game_type=resp["gameType"],
game=GAME_TYPES_TABLE.get(resp["gameType"]),
activated_at=datetime.utcfromtimestamp(resp["dateActivated"] / 1000),
stacked=resp.get("stacked"),
)
PK ! f< aiohypixel/friends.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Stuff for the friends endpoint
"""
__all__ = ("Friend",)
from dataclasses import dataclass
from datetime import datetime
from .shared import APIResponse, HypixelModel
@dataclass(frozen=True)
class Friend(HypixelModel):
sender_uuid: str
receiver_uuid: str
started_at: datetime
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Friend` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Friend` object.
"""
return cls(
sender_uuid=resp["uuidSender"],
receiver_uuid=resp["uuidReceiver"],
started_at=datetime.utcfromtimestamp(resp["started"] / 1000),
)
PK ! [ aiohypixel/guild.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Custom classes that are related to guild lookups on the Hypixel API
"""
__all__ = ("Guild", "GuildRank", "GuildMember", "GuildTag")
from dataclasses import dataclass
from datetime import datetime
from typing import Union, Dict, Tuple, List
from .shared import APIResponse, HypixelModel
LVL_EXP_NEEDED = [
100000,
150000,
250000,
500000,
750000,
1000000,
1250000,
1500000,
2000000,
2500000,
2500000,
2500000,
2500000,
2500000,
3000000,
]
def get_guild_level(exp: int) -> int:
level = 0
i = 0
while True:
need = LVL_EXP_NEEDED[i]
exp -= need
if exp < 0:
return level
level += 1
if i < len(LVL_EXP_NEEDED) - 1:
i += 1
@dataclass(frozen=True)
class GuildRank(HypixelModel):
name: str
default: bool
tag: Union[str, None]
created_at: datetime
priority: 1
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`GuildRank` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`GuildRank` object.
"""
return cls(
name=resp["name"],
default=resp["default"],
tag=resp["tag"],
created_at=datetime.utcfromtimestamp(resp["created"] / 1000),
priority=resp["priority"],
)
@dataclass(frozen=True)
class GuildMember(HypixelModel):
uuid: str
rank: str
joined_at: datetime
quest_participation: int
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`GuildMember` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`GuildMember` object.
"""
return cls(
uuid=resp["uuid"],
rank=resp["rank"],
joined_at=datetime.utcfromtimestamp(resp["joined"] / 1000),
quest_participation=resp.get("questParticipation", 0),
)
@dataclass(frozen=True)
class GuildTag(HypixelModel):
"""Represents a guild tag"""
text: str
colour: str
color: str
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`GuildTag` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`GuildTag` object.
"""
return cls(text=resp["tag"], colour=resp["tagColor"], color=resp["tagColor"])
@dataclass(frozen=True)
class Guild(HypixelModel):
"""Describes a Hypixel guild"""
raw_data: APIResponse
id: str
name: str
coins: int
total_coins: int
created_at: datetime
joinable: bool
tag: Dict[str, str]
exp: int
level: int
preferred_games: List[str]
ranks: Tuple[GuildRank]
members: Tuple[GuildMember]
banner: dict # The API is kinda messy on this one
achievements: Dict[str, int]
exp_by_game: Dict[str, int]
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Guild` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Guild` object.
"""
return cls(
raw_data=resp,
id=resp["_id"],
name=resp["name"],
coins=resp["coins"],
total_coins=resp["coinsEver"],
created_at=datetime.utcfromtimestamp(resp["created"] / 1000),
tag=GuildTag.from_api_response(resp),
exp=resp["exp"],
level=get_guild_level(resp["exp"]),
joinable=resp.get("joinable", False),
preferred_games=resp.get("preferredGames"),
ranks=tuple(GuildRank.from_api_response(r) for r in resp["ranks"]),
members=tuple(GuildMember.from_api_response(m) for m in resp["members"]),
banner=resp.get("banner"),
achievements=resp["achievements"],
exp_by_game=resp["guildExpByGameType"],
)
PK ! % % aiohypixel/keys.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Stuff for the keys endpoint
"""
__all__ = ("Key",)
from dataclasses import dataclass
from datetime import datetime
from .shared import APIResponse
@dataclass(frozen=True)
class Key:
owner_uuid: str
value: str # The actual key
total_queries: int
queries_in_past_min: int
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Key` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Key` object.
"""
return cls(
owner_uuid=resp["ownerUuid"],
value=resp["key"],
total_queries=resp["totalQueries"],
queries_in_past_min=resp.get("queriesInPastMin"),
)
PK ! : aiohypixel/leaderboards.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Leaderboards dataclasses
"""
__all__ = ("Leaderboards",)
from dataclasses import dataclass
from .shared import HypixelModel, APIResponse
@dataclass(frozen=True)
class Leaderboards(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Leaderboards` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Leaderboards` object.
"""
PK ! "%! %! aiohypixel/player.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Custom classes that are related to player lookups on the Hypixel API
"""
# TODO: Actually sort out this clusterfuck of stuff
from dataclasses import dataclass
from datetime import datetime
from math import sqrt, floor
from typing import Union, List, Dict, Tuple
from .shared import APIResponse, ImmutableProxy
from .stats import *
#: Locations where the player rank might be stored
POSSIBLE_RANK_LOC = ("packageRank", "newPackageRank", "monthlyPackageRank", "rank")
#: Level calculation stuff
EXP_FIELD = 0
LVL_FIELD = 0
BASE = 10000
GROWTH = 2500
HALF_GROWTH = 0.5 * GROWTH
REVERSE_PQ_PREFIX = -(BASE - 0.5 * GROWTH) / GROWTH
REVERSE_CONST = REVERSE_PQ_PREFIX * REVERSE_PQ_PREFIX
GROWTH_DIVIDES_2 = 2 / GROWTH
BEDWARS_EXP_PER_PRESTIGE = 489000
BEDWARS_LEVELS_PER_PRESTIGE = 100
def get_level(exp):
return floor(1 + REVERSE_PQ_PREFIX + sqrt(REVERSE_CONST + GROWTH_DIVIDES_2 * exp))
def get_exact_level(exp):
return get_level(exp) + get_percentage_to_next_lvl(exp)
def get_exp_from_lvl_to_next(level):
return GROWTH * (level - 1) + BASE
def get_total_exp_to_lvl(level):
lv = floor(level)
x0 = get_total_exp_to_full_lvl(lv)
if level == lv:
return x0
else:
return (get_total_exp_to_full_lvl(lv + 1) - x0) * (level % 1) + x0
def get_total_exp_to_full_lvl(level):
return (HALF_GROWTH * (level - 2) + BASE) * (level - 1)
def get_percentage_to_next_lvl(exp):
lv = get_level(exp)
x0 = get_total_exp_to_lvl(lv)
return (exp - x0) / (get_total_exp_to_lvl(lv + 1) - x0)
def get_exp(EXP_FIELD, LVL_FIELD):
exp = int(EXP_FIELD)
exp += get_total_exp_to_full_lvl(LVL_FIELD + 1)
return exp
@dataclass(frozen=True)
class Player:
"""
This will describe a player
"""
raw_data: APIResponse
hypixel_id: int
uuid: int
username: str
aliases: List[str] # in chronological order
one_time_achievements: List[str] # also in chronological order
achievments: ImmutableProxy
mc_version: str
rank: str
was_staff: bool
rank_colour: str
rank_color: str # for 'muricans
outfit: ImmutableProxy
voting: ImmutableProxy
parkours: ImmutableProxy
#: If it is `None`, it means the full player info wasn't requested
stats: Tuple[ImmutableProxy] = None
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Player` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Player` object.
"""
class PlayerStats:
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`PlayerStats` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`PlayerStats` object.
"""
class PlayerInfo:
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`PlayerStats` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`PlayerStats` object.
"""
def process_raw_player(json_data: APIResponse, partial: bool = False) -> Player:
"""
This handles the raw json returned by the API and creates a Player object
Pass the `partial` param as True to get a PartialPlayer object instead.
"""
# Basic player info
processed_data = {
"hypixel_id": json_data["_id"],
"uuid": json_data["uuid"],
"username": json_data["displayname"],
"aliases": [*json_data["knownAliases"].values()],
"raw_data": json_data,
}
# Last used MC version
processed_data.update({"mc_version": json_data.get("mcVersionRp")})
# Rank
for l in POSSIBLE_RANK_LOC:
if l in json_data:
if l == "rank" and json_data[l] == "NORMAL":
was_staff = True
else:
was_staff = False
if json_data[l].upper() == "NONE":
continue
rank = (
json_data[l]
.title()
.replace("_", " ")
.replace("Mvp", "MVP")
.replace("Vip", "VIP")
.replace("Superstar", "MVP++")
.replace("Youtuber", "YouTube")
.replace(" Plus", "+")
)
processed_data.update({"rank": rank, "was_staff": was_staff})
# Calculating player's Network EXP
processed_data.update({"level": get_level(json_data["networkExp"])})
# Voting stats
# Just processing the time snowflakes
# Doing a cheeky workaround :D
json_data["voting"] = ImmutableProxy(
{
k: datetime.utcfromtimestamp(v / 1000) if k.startswith("last") else v
for k, v in json_data.get("voting", {})
}
)
# That can be translated to:
# for k, v in json_data.get("voting", {}):
# if k.startswith("last"):
# json_data.get("voting", {})[k] = datetime.utcfromtimestamp(v / 1000)
# Parkours
# More cheeky trickery
p_actions = {
"timeStart": lambda t: datetime.utcfromtimestamp(t / 1000),
"timeTook": lambda t: t / 1000,
}
json_data["parkours"] = ImmutableProxy(
[ImmutableProxy({k: p_actions.get(k, lambda x: x)(v) for k, v in l}) for a in l]
for l in json_data.get("parkourCompletions", {})
)
# That can be translated to:
# p_tmp = {}
# for lobby in json_data.get("parkourCompletions", {}):
# new_attemps = []
# for attempt in lobby:
# new_attempt = {}
# for key, value in attempt:
# new_attempt[key] = p_actions.get(key, lambda x: x)(value)
# new_attempts.append(new_attempt)
# p_tmp[lobby] = new_attemps
# json_data["parkours"] = ImmutableProxy(p_tmp)
# Current outfit
processed_data.update(
{
"outfit": {
k.lower(): v.title().replace("_", " ") for k, v in json_data.get("outfit", {})
}
or None
}
)
if partial:
return Player(**processed_data)
processed_data.update({"stats": process_raw_player_stats(json_data.get("stats", {}))})
return Player(**processed_data)
### Stats ###
# This is extracted from Plancke's php stuff
BW_XP_PER_PRESTIGE = 489000
BW_LVLS_PER_PRESTIGE = 100
def get_bw_lvl(exp: int) -> int:
prestige = exp // BW_XP_PER_PRESTIGE
exp = exp % BW_XP_PER_PRESTIGE
if prestige > 5:
over = prestige % 5
exp += over * BW_XP_PER_PRESTIGE
prestige -= over
if exp < 500:
return 0 + (prestige * BW_LVLS_PER_PRESTIGE)
if exp < 1500:
return 1 + (prestige * BW_LVLS_PER_PRESTIGE)
if exp < 3500:
return 2 + (prestige * BW_LVLS_PER_PRESTIGE)
if exp < 5500:
return 3 + (prestige * BW_LVLS_PER_PRESTIGE)
if exp < 9000:
return 4 + (prestige * BW_LVLS_PER_PRESTIGE)
exp -= 9000
return (exp / 5000 + 4) + (prestige * BW_LVLS_PER_PRESTIGE)
def process_raw_player_stats(
json_data: APIResponse, game: Union[str, None] = None
) -> Tuple[ImmutableProxy]:
"""This will process the JSON data into a tuple of game stats"""
# Processing bedwars exp
json_data.get("Bedwars", {})["level"] = get_bw_lvl(json_data.get("Bedwars", {})["Experience"])
... # Process more stuff in the future ig
return tuple(ImmutableProxy(d) for _, d in json_data.items())
PK ! t aiohypixel/resources.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Resources dataclasses
"""
__all__ = (
"Achievements",
"Quests",
"Challenges",
"GuildAchievements",
"GuildPermissions",
"SkyblockCollections",
"SkyblockSkills",
)
from dataclasses import dataclass
from .shared import HypixelModel, APIResponse
@dataclass(frozen=True)
class Achievements(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Achievements` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Achievements` object.
"""
@dataclass(frozen=True)
class Quests(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Quests` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Quests` object.
"""
@dataclass(frozen=True)
class Challenges(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`Challenges` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`Challenges` object.
"""
@dataclass(frozen=True)
class GuildAchievements(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`GuildAchievements` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`GuildAchievements` object.
"""
@dataclass(frozen=True)
class GuildPermissions(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`GuildPermissions` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`GuildPermissions` object.
"""
@dataclass(frozen=True)
class SkyblockCollections(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`SkyblockCollections` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`SkyblockCollections` object.
"""
@dataclass(frozen=True)
class SkyblockSkills(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`SkyblockSkills` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`SkyblockSkills` object.
"""
PK ! JvP P aiohypixel/session.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
This defines a connection to the Hypixel API.
All requests will pass through here and this will handle everything for you
"""
# TODO: Clean up logging messages.
__all__ = ("HypixelSession",)
import aiohttp
import asyncio
import logging
import typing
from random import choice
import re
from uuid import UUID
# NOTE: I should probably ditch the wildcard import
from .shared import *
from .player import *
from .guild import *
from .boosters import *
from .keys import *
from .friends import *
from .watchdogstats import *
from .resources import *
from .skyblock import *
from .leaderboards import *
#: This is the base URL for all the requests regarding the Hypixel API.
BASE_API_URL = "https://api.hypixel.net/"
#: This is the message sent when you try to do a request with an invalid key.
INVALID_KEY_MESSAGE = "Invalid API key!"
#: Mojang name lookup endpoint
MOJANG_PROFILE_API_URL = "https://api.mojang.com/users/profiles/minecraft/"
#: This is th length of a UUID. 32 hex digits + 4 dashes.
UUID_LENGTH = (32, 36)
#: This is the boundaries of char lenghts for Minecraft usernames.
MC_NAME_LENGTH = (3, 16)
#: MongoDB _id string pattern. The API uses Mongo's IDs for guilds.
MONGO_ID_RE = re.compile(r"^[a-f\d]{24}$", re.I)
class HypixelSession:
"""
Represents a connection to the Hypixel API.
You can perform a check on your API keys with the :meth:`check_keys` method.
"""
#: This is the base URL for all the requests regarding the Hypixel API. Also see :const:`BASIC_API_URL`.
DEFAULT_BASE_API_URL: str = BASE_API_URL
__slots__ = ("logger", "_api_keys", "api_url", "max_key_wait_time", "loop", "http_client")
def __init__(
self,
api_keys: typing.Iterable[str],
*,
api_url: str = None,
max_key_wait_time: typing.Union[int, float] = 10,
loop: asyncio.AbstractEventLoop = None,
) -> None:
"""
Args:
api_keys:
The collection of API keys to use for making requests.
api_url:
The base API URL to use. Defaults to :attr:`DEFAULT_BASE_API_URL`.
max_key_wait_time:
The maximum time in seconds the requester will wait for an available key.
After that, :exc:asyncio.TimeoutError is raised.
Defaults to 10.
loop:
The event loop to use for any asynchronous task.
"""
#: Client logger.
self.logger = logging.getLogger(__name__)
self.logger.debug("Starting a new session...")
#: Pool of API keys to use when doing requests.
self._api_keys = asyncio.Queue()
# Filling the queue
for k in api_keys:
self._api_keys.put_nowait(k)
#: Base API URL to use
self.api_url = api_url or self.DEFAULT_BASE_API_URL
#: Maximum time (in seconds) allowed for the requester to wait for an available key before raising an error.
self.max_key_wait_time = max_key_wait_time
#: Event loop to use for any asynchronous process.
self.loop = loop or asyncio.get_event_loop()
#: Client to use for making requests to the API.
self.http_client = aiohttp.ClientSession(loop=self.loop)
self.logger.debug("Session fully initialized!")
@property
def api_keys(self):
"""Set of all available keys in the pool."""
# Ik this isn't that great...
return set(self._api_keys._queue)
async def _request(self, endpoint: str, params: dict = {}):
"""
Fetches the JSON response from the passed endpoint with the given params.
Args:
endpoint:
The endpoint to make the request to.
params:
The query-string parameters to pass.
Returns:
The JSON response received from the API.
Raises:
asyncio.TimeoutError:
In case an available key cannot be found in less time than :attr:`max_key_wait_time` seconds.
InvalidAPIKey:
If the API key used was rejected.
When this happens, the invalid API key will be removed from the internal key pool.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
self.logger.debug(
"Starting to fetch JSON from the **%s** endpoint with these params: %s",
endpoint,
params,
)
self.logger.debug("Chosing key...")
key = await asyncio.wait_for(self._api_keys.get(), timeout=self.max_key_wait_time)
if "key" not in params:
params["key"] = key
self.logger.debug("Chose: `%s`\nNow doing the actual fetching...", params["key"])
trash_key = False
try:
async with self.http_client.get(f"{BASE_API_URL}{endpoint}", params=params) as resp:
resp.raise_for_status()
result = await resp.json()
if not result["success"]:
# NOTE: Maybe keep trying until a key is valid?
if result["cause"] == INVALID_KEY_MESSAGE:
trash_key = True
self.logger.error("Request failed due to invalid API key! Removing it...")
raise InvalidAPIKey
self.logger.error("Request failed! Cause: %s", result["cause"])
raise UnsuccessfulRequest(result["cause"])
self.logger.debug("Request was successful! Returning result...")
return result
except KeyError:
raise HypixelAPIError("Something went wrong with the Hypixel API! Try again later.")
finally:
if not trash_key:
self._api_keys.put_nowait(key)
async def check_keys(
self, keys: typing.Union[str, typing.Iterable[str]]
) -> typing.Iterable[str]:
"""
Validates the given keys by using the '/key' endpoint, wrapped by :meth:`get_key`.
Args:
keys:
Collection of keys to check.
Returns:
Collection of invalid keys.
Raises:
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
self.logger.debug("Checking API keys: %s", keys)
rejected_keys = set()
for k in keys:
try:
# Not even caring about the return value
await self.get_key(k)
except InvalidAPIKey:
rejected_keys.add(k)
self.logger.debug(
"Finished checking API keys. Encoutered %s error%s!",
len(rejected_keys) or "no",
"s" if rejected_keys else "",
)
return rejected_keys
## Deprecated this since it doesn't make sense with the queue aproach I'm going with
## I might add separate methods for adding or removing keys later.
# async def edit_keys(
# self, new_keys: typing.Iterable[str], validate_keys: bool = True
# ) -> typing.Union[typing.Set[str], None]:
# """
# Allows you to edit your registered keys for this session.
# You can optionally perform a check on the keys, and if so,
# any invaid key will not be added and instead be returned a collection of all invalid keys.
# Args:
# new_keys:
# The collection of keys to check.
# validate_keys:
# Wheather to perform validation checks on the keys or not.
# Returns:
# Collection of invalid keys.
# Raises:
# HypixelAPIError:
# In case the API didn't give an OK response.
# UnsuccessfulRequest:
# If the API rejected the request for some unkonwn reason.
# """
# self.logger.debug("Attempting to change the API key pool...")
# invalid_keys = None
# if validate_keys:
# invalid_keys = await self.check_keys(new_keys)
# self.api_keys = self.api_keys - invalid_keys
# self.logger.info("Changed the API key pool to: %s", self.api_keys)
# return invalid_keys
async def name_to_uuid(self, query: str) -> str:
"""
Gets the UUID for the player with the given name.
Uses the Mojang API.
Args:
query:
The username you want to lookup the UUID of.
Returns:
The player's UUID.
Raises:
aiohttp.ClientResponseError:
In case something goes wrong with the request.
"""
self.logger.debug("Fetching UUID from name: %s", query)
async with self.http_client.get(f"{MOJANG_PROFILE_API_URL}{query}") as resp:
resp.raise_for_status()
return (await resp.json())["id"]
async def uuid_to_name(self, query: str) -> str:
"""
Gets the name for the player with the given UUID.
Uses the Mojang API for that.
Args:
query:
The player UUID to get the name of.
Returns:
The player's name.
Raises:
aiohttp.ClientResponseError:
In case something goes wrong with the request.
"""
self.logger.debug("Fetching name from UUID: %s", query)
async with self.http_client.get(f"{MOJANG_PROFILE_API_URL}{query}") as resp:
resp.raise_for_status()
return (await resp.json())["name"]
async def get_player_by_name(self, name: str) -> typing.Union[Player, None]:
""""""
async def get_player_by_uuid(self, uuid: typing.Union[UUID, str]) -> typing.Union[Player, None]:
""""""
async def get_player(self, query: typing.Union[str, UUID]) -> typing.Union[Player, None]:
"""
Gets a full player object (general info + game stats) with the given query.
Args:
query:
Either the UUID or username of the player you want to get.
Returns:
The requested player data or `None` if the player wasn't found.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
if is_uuid(query):
return await self.get_player_by_uuid(query)
return await self.get_player_by_name(query)
async def get_player_stats(self, query: str) -> typing.Union[PlayerStats, None]:
"""
Gets a player's stats with the given query.
Args:
query:
Either the UUID or username of the player you want to get the stats for.
Returns:
The requested player stats or `None` if the player wasn't found.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
if is_uuid(query):
query_type = "uuid"
else:
query_type = "name"
result = await self._request("player", params={query_type: query})
if result["player"] is None:
return
return PlayerStats.from_api_response(result["player"]["stats"])
async def get_player_info(self, query: str) -> typing.Union[PlayerInfo, None]:
"""
Gets a player object with just the network info that does not relate to any stats.
Args:
query:
Either the UUID or username of the player you want to get info for.
Returns:
The requested player info or `None` if the player wasn't found.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
if is_uuid(query):
query_type = "uuid"
else:
query_type = "name"
result = await self._request("player", params={query_type: query})
if result["player"] is None:
return
return PlayerInfo.from_api_response(result["player"])
# NOTE: maybe split this into two separate methods?
async def get_guild_id(self, query: str) -> typing.Union[str, None]:
""""""
async def get_guild_by_id(self, guild_id: str) -> typing.Union[Guild, None]:
""""""
async def get_guild_by_name(self, name: str) -> typing.Union[Guild, None]:
""""""
async def get_guild(self, query: str) -> typing.Union[Guild, None]:
"""
Gets a guild object from the given query.
Args:
query:
Either the ID or name of the guild you want to get.
Returns:
The requested guild or `None` if not found.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
if is_uuid(query):
return await self.get_guild_by_id(query)
return await self.get_guild_by_name(query)
# NOTE: Maybe rename to 'get_player_guild'?
async def get_guild_by_player_uuid(
self, uuid: typing.Union[UUID, str]
) -> typing.Union[Guild, None]:
""""""
async def get_guild_by_player_name(self, name: str) -> typing.Union[Guild, None]:
""""""
async def get_guild_by_player(
self, query: typing.Union[str, UUID]
) -> typing.Union[Guild, None]:
"""
Gets a guild object to which the player with the given name/UUID belongs to.
Args:
query:
Either the ID or name of the player you want to get guild for.
Returns:
The requested guild or `None` if not found.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
if is_uuid(query):
return await self.get_guild_by_player_uuid(query)
return await self.get_guild_by_player_name(query)
async def get_key(self, key: typing.Union[UUID, str]) -> Key:
"""
Gets a Key object from the passed query.
Args:
key:
The key to get.
Returns:
The requested key.
Raises:
InvalidAPIKey:
If the requested key does not exist.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
if not is_uuid(key):
raise InvalidAPIKey(f"{key} isn't a valid UUID and so it isn't a valid key!")
result = await self._request("key", params={"key": key})
return Key.from_api_response(result["record"])
async def get_friends_by_name(self, name: str) -> typing.List[Friend]:
""""""
async def get_friends_by_uuid(self, uuid: typing.Union[UUID, str]) -> typing.List[Friend]:
""""""
# NOTE: Maybe implement an async iterator for this?
async def get_friends(self, query: typing.Union[str, UUID]) -> typing.List[Friend]:
"""
Gets a tuple of Friend objects that the player with the given name/UUID has.
Args:
query:
Either the UUID or the username of the player you want to get the friends for.
Returns:
A list of friends the given player has.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
if is_uuid(uuid):
return await self.get_friends_by_uuid(query)
return await self.get_friends_by_name(query)
async def get_boosters(self) -> typing.Tuple[typing.List[Booster], dict]:
"""
Gets a tuple of Booster objects denoting the currently active boosters on the Hypixel Network.
Returns:
A list containing the currently active boosters as well as the boosters state.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
result = await self._request("boosters")
boosters = [Booster.from_api_response(b) for b in result.get("boosters", ())]
# NOTE: Maybe tidy up this with an enum or smth?
return boosters, result.get("boosterState")
async def get_watchdog_stats(self) -> WatchdogStats:
"""
Gets the current WatchDog stats.
Returns:
The current Watchdog stats.
Raises:
InvalidAPIKey:
If the used API key isn't valid.
HypixelAPIError:
In case the API didn't give an OK response.
UnsuccessfulRequest:
If the API rejected the request for some unkonwn reason.
"""
result = await self._request("watchdogstats")
return WatchdogStats.from_api_response(result)
async def get_leaderboards(self) -> Leaderboards:
""""""
async def get_player_count(self) -> int:
""""""
async def get_achievements_resource(self) -> Achievements:
""""""
async def get_quests_resource(self) -> Quests:
""""""
async def get_challenges_resource(self) -> Challenges:
""""""
async def get_guild_achievements_resource(self) -> GuildAchievements:
""""""
async def get_guild_permissions_resource(self) -> GuildPermissions:
""""""
async def get_skyblock_collections_resource(self) -> SkyblockCollections:
""""""
async def get_skyblock_skills_resource(self) -> SkyblockSkills:
""""""
async def get_skyblock_player_auctions_by_uuid(
self, uuid: typing.Union[UUID, str]
) -> typing.List[SkyblockAuction]:
""""""
async def get_skyblock_profile_auctions(
self, uuid: typing.Union[UUID, str]
) -> typing.List[SkyblockAuction]:
""""""
async def get_skyblock_auction(self, uuid: typing.Union[UUID, str]) -> SkyblockAuction:
""""""
async def get_all_skyblock_auctions(self) -> SkyblockAuctionsIterator:
""""""
async def get_all_skyblock_auctions_page(self, index: int) -> typing.List[SkyblockAuction]:
""""""
async def get_skyblock_news(self) -> typing.List[SkyblockNews]:
""""""
async def get_skyblock_profile(self) -> SkyblockProfile:
""""""
PK ! MQ# # aiohypixel/shared.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Custom exceptions related to the Hypixel API and other misc stuff
"""
__all__ = (
"GAME_TYPES_TABLE",
"APIResponse",
"AiohypixelException",
"UnsuccessfulRequest",
"PlayerNotFound",
"GuildIDNotValid",
"HypixelAPIError",
"InvalidAPIKey",
"ImmutableProxy",
"is_uuid",
)
import abc
import inspect
import typing
import uuid
#: Mapping for decoding game types (https://github.com/HypixelDev/PublicAPI/blob/master/Documentation/misc/GameType.md)
GAME_TYPES_TABLE = {
2: {"type_name": "QUAKECRAFT", "db_name": "Quake", "pretty_name": "Quake"},
3: {"type_name": "WALLS", "db_name": "Walls", "pretty_name": "Walls"},
4: {"type_name": "PAINTBALL", "db_name": "Paintball", "pretty_name": "Paintball"},
5: {
"type_name": "SURVIVAL_GAMES",
"db_name": "HungerGames",
"pretty_name": "Blitz Survival Games",
},
6: {"type_name": "TNTGAMES", "db_name": "TNTGames", "pretty_name": "TNT Games"},
7: {"type_name": "VAMPIREZ", "db_name": "VampireZ", "pretty_name": "VampireZ"},
13: {"type_name": "WALLS3", "db_name": "Walls3", "pretty_name": "Mega Walls"},
14: {"type_name": "ARCADE", "db_name": "Arcade", "pretty_name": "Arcade"},
17: {"type_name": "ARENA", "db_name": "Arena", "pretty_name": "Arena"},
20: {"type_name": "UHC", "db_name": "UHC", "pretty_name": "UHC Champions"},
21: {"type_name": "MCGO", "db_name": "MCGO", "pretty_name": "Cops and Crims"},
23: {"type_name": "BATTLEGROUND", "db_name": "Battleground", "pretty_name": "Warlords"},
24: {"type_name": "SUPER_SMASH", "db_name": "SuperSmash", "pretty_name": "Smash Heroes"},
25: {"type_name": "GINGERBREAD", "db_name": "GingerBread", "pretty_name": "Turbo Kart Racers"},
26: {"type_name": "HOUSING", "db_name": "Housing", "pretty_name": "Housing"},
51: {"type_name": "SKYWARS", "db_name": "SkyWars", "pretty_name": "SkyWars"},
52: {"type_name": "TRUE_COMBAT", "db_name": "TrueCombat", "pretty_name": "Crazy Walls"},
54: {"type_name": "SPEED_UHC", "db_name": "SpeedUHC", "pretty_name": "Speed UHC"},
55: {"type_name": "SKYCLASH", "db_name": "SkyClash", "pretty_name": "SkyClash"},
56: {"type_name": "LEGACY", "db_name": "Legacy", "pretty_name": "Classic Games"},
57: {"type_name": "PROTOTYPE", "db_name": "Prototype", "pretty_name": "Prototype"},
58: {"type_name": "BEDWARS", "db_name": "Bedwars", "pretty_name": "Bed Wars"},
59: {
"type_name": "MURDER_MYSTERY",
"db_name": "MurderMystery",
"pretty_name": "Murder Mystery",
},
60: {"type_name": "BUILD_BATTLE", "db_name": "BuildBattle", "pretty_name": "Build Battle"},
61: {"type_name": "DUELS", "db_name": "Duels", "pretty_name": "Duels"},
}
#: Dummy type to represent an response from the Hypiel API.
APIResponse = typing.NewType("APIResponse", typing.Dict[str, typing.Any])
class AiohypixelException(Exception):
"""
Base exception class from which all other exceptions by this library are subclassed.
"""
class UnsuccessfulRequest(AiohypixelException):
"""
Raised when the "success" key from a request is False
"""
class PlayerNotFound(UnsuccessfulRequest):
"""
Raised if a player/UUID is not found. This exception can usually be ignored.
You can catch this exception with ``except aiohypixel.PlayerNotFoundException:``
"""
class GuildIDNotValid(UnsuccessfulRequest):
"""
Raised if a Guild is not found using a Guild ID. This exception can usually be ignored.
You can catch this exception with ``except aiohypixel.GuildIDNotValid:``
"""
class HypixelAPIError(UnsuccessfulRequest):
"""
Raised if something's gone very wrong with the API.
"""
class InvalidAPIKey(UnsuccessfulRequest):
"""
Raised if the given API Key is invalid
"""
def find(func, iterable):
for i in iterable:
if func(i):
return i
return None
def get(iterable, **attrs):
def predicate(elem):
for attr, val in attrs.items():
nested = attr.split("__")
obj = elem
for attribute in nested:
obj = getattr(obj, attribute)
if obj != val:
return False
return True
return find(predicate, iterable)
def is_uuid(item: typing.Union[str, uuid.UUID]) -> bool:
"""
Checks if the given string is a valid UUID.
Args:
item:
The hexadecimal string to check.
"""
if isinstance(item, str):
try:
uuid.UUID(item)
return True
except ValueError: # in case of a malformed hex UUID string
return False
return isinstance(item, uuid.UUID)
class ImmutableProxy:
""""""
class HypixelModel(abc.ABC):
"""
Base for all models.
"""
@abc.abstractclassmethod
def from_api_response(cls, resp: APIResponse):
...
PK ! .!e aiohypixel/skyblock.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Skyblock related stuff
"""
__all__ = ("SkyblockAuction", "SkyblockNews", "SkyblockProfile", "SkyblockAuctionsIterator")
import collections
from dataclasses import dataclass
from .shared import HypixelModel, APIResponse
@dataclass(frozen=True)
class SkyblockAuction(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`SkyblockAuction` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`SkyblockAuction` object.
"""
@dataclass(frozen=True)
class SkyblockNews(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`SkyblockNews` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`SkyblockNews` object.
"""
@dataclass(frozen=True)
class SkyblockProfile(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`SkyblockProfile` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`SkyblockProfile` object.
"""
class SkyblockAuctionsIterator(collections.abc.AsyncIterator):
""""""
PK ! c!$ aiohypixel/stats.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Stats dataclasses and other related functions
"""
__all__ = ("BaseGameStats", "BedWarsStats")
from dataclasses import dataclass
from .shared import HypixelModel, APIResponse
class BaseGameStats(HypixelModel):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
...
@dataclass(frozen=True)
class BedWarsStats(BaseGameStats):
""""""
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`BedWarsStats` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`BedWarsStats` object.
"""
PK ! g# aiohypixel/watchdogstats.py#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright © Tmpod 2019
#
# This file is part of aiohypixel.
#
# aiohypixel is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# aiohypixel is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with aiohypixel. If not, see .
"""
Stuff for the Watchdog Stats endpoint
"""
__all__ = ("WatchdogStats",)
from dataclasses import dataclass
from .shared import APIResponse
@dataclass(frozen=True)
class WatchdogStats:
total: int
rolling_daily: int
last_minute: int
staff_total: int
staff_rolling_daily: int
@classmethod
def from_api_response(cls, resp: APIResponse):
"""
Processes the raw API response into a :class:`WatchdogStats` object.
Args:
resp:
The API response to process.
Returns:
The processed :class:`WatchdogStats` object.
"""
return cls(
total=resp["watchdog_total"],
rolling_daily=resp["watchdog_rollingDaily"],
last_minute=resp["watchdog_lastMinute"],
staff_total=resp["staff_total"],
staff_rolling_daily=resp["staff_rollingDaily"],
)
PK ! S % aiohypixel-0.2.0.dist-info/LICENSE.md GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.
PK !HڽT U aiohypixel-0.2.0.dist-info/WHEEL
A
н#Z;/"d&F[xzw@Zpy3Fv]\fi4WZ^EgM_-]#0(q7PK !HX˅~ # aiohypixel-0.2.0.dist-info/METADATAT]oF|_jFi
!l5,$N /&!/%-KN ywd!+3x.2ec+(}ćX5L4HtFd1ONZvNEJŵq`z||Q{0Zc1u=C?Jdqz!T{VH:epa
kb_?'JW
qUv;=)pfd]+ԛHԹbDžO_ŧ|QKCOY1m3n^Qsl go&නB}wi!N b)zJXǪ!)8AVd>BLoB^նgw".P;P*_\{254&N