from datetime import datetime
from typing import Any, Dict, List, MutableMapping, Optional, TypeVar
KT = TypeVar("KT")
VT = TypeVar("VT")
Colors = Dict[str, int]
Colours = Colors
def camel_to_snake(string: str) -> str:
return "".join(["_" + c.lower() if c.isupper() else c for c in string]).lstrip("_")
def parse_vote_dict(d: dict) -> dict:
data = d.copy()
query = data.get("query", "").lstrip("?")
if query:
query_dict = {k: v for k, v in [pair.split("=") for pair in query.split("&")]}
data["query"] = DataDict(**query_dict)
else:
data["query"] = {}
if "bot" in data:
data["bot"] = int(data["bot"])
elif "guild" in data:
data["guild"] = int(data["guild"])
for key, value in data.copy().items():
converted_key = camel_to_snake(key)
if key != converted_key:
del data[key]
data[converted_key] = value
return data
def parse_dict(d: dict) -> dict:
data = d.copy()
for key, value in data.copy().items():
if "id" in key.lower():
if value == "":
value = None
else:
if isinstance(value, str) and value.isdigit():
value = int(value)
else:
continue
elif value == "":
value = None
converted_key = camel_to_snake(key)
if key != converted_key:
del data[key]
data[converted_key] = value
return data
def parse_bot_dict(d: dict) -> dict:
data = parse_dict(d.copy())
if data.get("date") and not isinstance(data["date"], datetime):
data["date"] = datetime.strptime(data["date"], "%Y-%m-%dT%H:%M:%S.%fZ")
if data.get("owners"):
data["owners"] = [int(e) for e in data["owners"]]
if data.get("guilds"):
data["guilds"] = [int(e) for e in data["guilds"]]
for key, value in data.copy().items():
converted_key = camel_to_snake(key)
if key != converted_key:
del data[key]
data[converted_key] = value
return data
def parse_user_dict(d: dict) -> dict:
data = d.copy()
data["social"] = SocialData(**data.get("social", {}))
return data
def parse_bot_stats_dict(d: dict) -> dict:
data = d.copy()
if "server_count" not in data:
data["server_count"] = None
if "shards" not in data:
data["shards"] = []
if "shard_count" not in data:
data["shard_count"] = None
return data
[docs]class DataDict(dict, MutableMapping[KT, VT]):
"""Base class used to represent received data from the API.
Every data model subclasses this class.
"""
def __init__(self, **kwargs: VT) -> None:
super().__init__(**parse_dict(kwargs))
self.__dict__ = self
[docs]class BotData(DataDict[str, Any]):
"""Model that contains information about a listed bot on top.gg. The data this model contains can be found `here
<https://docs.top.gg/api/bot/#bot-structure>`__."""
id: int
username: str
discriminator: str
avatar: Optional[str]
def_avatar: str
prefix: str
shortdesc: str
longdesc: Optional[str]
tags: List[str]
website: Optional[str]
support: Optional[str]
github: Optional[str]
owners: List[int]
guilds: List[int]
invite: Optional[str]
date: datetime
certified_bot: bool
vanity: Optional[str]
points: int
monthly_points: int
donatebotguildid: int
def __init__(self, **kwargs: Any):
super().__init__(**parse_bot_dict(kwargs))
[docs]class BotStatsData(DataDict[str, Any]):
"""Model that contains information about a listed bot's guild and shard count."""
server_count: Optional[int]
"""The amount of servers the bot is in."""
shards: List[int]
"""The amount of servers the bot is in per shard."""
shard_count: Optional[int]
"""The amount of shards a bot has."""
def __init__(self, **kwargs: Any):
super().__init__(**parse_bot_stats_dict(kwargs))
[docs]class BriefUserData(DataDict[str, Any]):
"""Model that contains brief information about a Top.gg user."""
id: int
"""The Discord ID of the user."""
username: str
"""The Discord username of the user."""
avatar: str
"""The Discord avatar URL of the user."""
def __init__(self, **kwargs: Any):
if kwargs["id"].isdigit():
kwargs["id"] = int(kwargs["id"])
super().__init__(**kwargs)
[docs]class SocialData(DataDict[str, str]):
"""Model that contains social information about a top.gg user."""
youtube: str
"""The YouTube channel ID of the user."""
reddit: str
"""The Reddit username of the user."""
twitter: str
"""The Twitter username of the user."""
instagram: str
"""The Instagram username of the user."""
github: str
"""The GitHub username of the user."""
[docs]class UserData(DataDict[str, Any]):
"""Model that contains information about a top.gg user. The data this model contains can be found `here
<https://docs.top.gg/api/user/#structure>`__."""
id: int
username: str
discriminator: str
social: SocialData
color: str
supporter: bool
certified_dev: bool
mod: bool
web_mod: bool
admin: bool
def __init__(self, **kwargs: Any):
super().__init__(**parse_user_dict(kwargs))
[docs]class VoteDataDict(DataDict[str, Any]):
"""Base model that represents received information from Top.gg via webhooks."""
type: str
"""Type of the action (``upvote`` or ``test``)."""
user: int
"""ID of the voter."""
query: DataDict
"""Query parameters in :ref:`DataDict`."""
def __init__(self, **kwargs: Any):
super().__init__(**parse_vote_dict(kwargs))
[docs]class BotVoteData(VoteDataDict):
"""Model that contains information about a bot vote."""
bot: int
"""ID of the bot the user voted for."""
is_weekend: bool
"""Boolean value indicating whether the action was done on a weekend."""
[docs]class ServerVoteData(VoteDataDict):
"""Model that contains information about a server vote."""
guild: int
"""ID of the guild the user voted for."""