diff --git a/discord/__init__.py b/discord/__init__.py index 1e74cf91..818abc4a 100644 --- a/discord/__init__.py +++ b/discord/__init__.py @@ -68,7 +68,6 @@ class VersionInfo(NamedTuple): releaselevel: Literal["alpha", "beta", "candidate", "final"] serial: int - version_info: VersionInfo = VersionInfo(major=2, minor=0, micro=0, releaselevel='alpha', serial=0) logging.getLogger(__name__).addHandler(logging.NullHandler()) diff --git a/discord/colour.py b/discord/colour.py index 927addc1..e0bcf700 100644 --- a/discord/colour.py +++ b/discord/colour.py @@ -83,6 +83,24 @@ class Colour: raise TypeError(f'Expected int parameter, received {value.__class__.__name__} instead.') self.value: int = value + + @staticmethod + def from_base(self, value: str, base:int = None): + """ + Initiate self.value from different base(hexidecimal, binary) + ======= + + value `str` : + + value in different base, e.g. white in hexidecimal, 0xffffff + + base `int` (optional) : + + base of your value, if you don't supply this, you have to add a prefix to your number, e.g. 0x or 0b + + """ + + return Colour(value = int(value, base=base)) def _get_byte(self, byte: int) -> int: return (self.value >> (8 * byte)) & 0xff diff --git a/discord/embeds.py b/discord/embeds.py index 25f05aef..c118aadc 100644 --- a/discord/embeds.py +++ b/discord/embeds.py @@ -315,13 +315,28 @@ class Embed: return getattr(self, '_colour', EmptyEmbed) @colour.setter - def colour(self, value: Union[int, Colour, _EmptyEmbed]): # type: ignore + def colour(self, value: Union[int, Colour, _EmptyEmbed, str], base:int=None): # type: ignore + """ + Set colour + ============ + + value `Union[int, Colour, _EmptyEmbed, str]`: + + value of the colour. If you want to use different number systems such as hexidecimal or binary, use Colour.from_base or set it as a string in that system + + base `int` (optional): + + if value is a string without a prefix(0x, 0b), set base here + """ + if isinstance(value, (Colour, _EmptyEmbed)): self._colour = value elif isinstance(value, int): self._colour = Colour(value=value) + elif isinstance(value, str): + self._colour = int(value, base=base) else: - raise TypeError(f'Expected discord.Colour, int, or Embed.Empty but received {value.__class__.__name__} instead.') + raise TypeError(f'Expected discord.Colour, int, str, or Embed.Empty but received {value.__class__.__name__} instead.') color = colour diff --git a/discord/errors.py b/discord/errors.py index bc2398d5..9b33c260 100644 --- a/discord/errors.py +++ b/discord/errors.py @@ -143,7 +143,7 @@ class HTTPException(DiscordException): self.code = 0 fmt = '{0.status} {0.reason} (error code: {1})' - if len(self.text): + if self.text: fmt += ': {2}' super().__init__(fmt.format(self.response, self.code, self.text)) diff --git a/discord/ext/commands/cog.py b/discord/ext/commands/cog.py index 9931557d..3d3dc1c0 100644 --- a/discord/ext/commands/cog.py +++ b/discord/ext/commands/cog.py @@ -208,7 +208,7 @@ class Cog(metaclass=CogMeta): for command in self.__cog_commands__: setattr(self, command.callback.__name__, command) parent = command.parent - if parent is not None: + if parent: # Get the latest parent reference parent = lookup[parent.qualified_name] # type: ignore @@ -230,7 +230,7 @@ class Cog(metaclass=CogMeta): This does not include subcommands. """ - return [c for c in self.__cog_commands__ if c.parent is None] + return [c for c in self.__cog_commands__ if not c.parent] @property def qualified_name(self) -> str: diff --git a/discord/ext/commands/errors.py b/discord/ext/commands/errors.py index 93834385..9ea1e81a 100644 --- a/discord/ext/commands/errors.py +++ b/discord/ext/commands/errors.py @@ -110,7 +110,7 @@ class CommandError(DiscordException): from :class:`.Bot`\, :func:`.on_command_error`. """ def __init__(self, message: Optional[str] = None, *args: Any) -> None: - if message is not None: + if message: # replace 'if not none' with 'if message' # clean-up @everyone and @here mentions m = message.replace('@everyone', '@\u200beveryone').replace('@here', '@\u200bhere') super().__init__(m, *args) diff --git a/discord/ext/json/__init__.py b/discord/ext/json/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/discord/ext/json/database.py b/discord/ext/json/database.py new file mode 100644 index 00000000..de2503f4 --- /dev/null +++ b/discord/ext/json/database.py @@ -0,0 +1,50 @@ +from .types import JSONFile, Entry +from typing import Union, Any + +class Database: + def __init__(self, file:JSONFile): + self.file = file; + self.database = {}; + + async def getData(self) -> dict: + """ + Get contents from a JSONFile + """ + + contents = await self.file.serialize("load") + + return contents + + async def dumpData(self, data:dict) -> None: + """ + Dump a dict into file + ===== + + data `dict` - + + The data to be dumped into the file. + """ + + await self.file.serialize("dump", contents=data) + + async def loadFile(self) -> None: + """ + Load JSON from file to self.database + """ + + self.database = await self.getData(); + + async def getEntry(self, name:str) -> Entry: + value : Union[Any] = self.database[name] + + return Entry(name, value) + + async def editEntry(self, name:str, value:Union[Any]): + self.database[name] = value; + + async def saveData(self): + """ + Save current database to file + """ + + await self.file.serialize("dump", contents=self.database) \ No newline at end of file diff --git a/discord/ext/json/types.py b/discord/ext/json/types.py new file mode 100644 index 00000000..de4dc0d7 --- /dev/null +++ b/discord/ext/json/types.py @@ -0,0 +1,137 @@ +import json + +from typing import Union, Any + + +class File: + def __init__(self, path:str): + """ + Initialize a file. + + ======= + + path `str` : + + path to the file + """ + + self.path : str = path + + @staticmethod + def construct(path:str): + """ + Construct a File object statically + """ + + if not isinstance(path, str): + raise TypeError(f"Expected path to be type str, got {type(path)} instead.") + + return File(path) + + async def access(self, mode:str): + """ + Open object to access file. Don't use this in your code. + """ + + if not isinstance(mode, str): + raise TypeError(f"Expected mode to be type str, got {type(mode)} instead.") + + return open(self.path, mode) + + async def open_writer(self): + """Open plain writer for file""" + + return await self.access("w+") + + async def open_reader(self): + """Open plain reader for file""" + + return await self.access("r+") + + async def open_binary_writer(self): + """Open binary writer for file""" + + return await self.access("wb+") + + async def open_binary_reader(self): + """Open binary reader for file""" + + return await self.access("rb+") + + async def read_contents(self, binary=False) -> Union[str, bytes]: + """ + Dump out file contents + ====== + + binary `bool` (optional): + + Set to False normally. Controls whether to read file as binary or read as normal text. + """ + + reader = None # initialize + + if binary: + reader = await self.open_binary_reader() + else: + reader = await self.open_reader() + + contents = reader.read() + + reader.close() # safely close file + + return contents + + async def serialize(self, serializer, *args, **kwargs) -> Union[Any]:# + return serializer(*args, **kwargs) + +class JSONFile(File): + load = json.load + dump = json.dump + + async def serialize(self, serializer:str, *args, **kwargs) -> Union[dict, int]: + """ + Serialize JSON data + ===== + + serializer `str`: + + dump or load data, set to "load" for loading file, "dump" to dump json to file + + contents `dict` of kwargs: + + what to dump. + """ + + if not serializer: + raise ValueError("Argument serializer should be either load or dump, got NoneType instead.") + + if serializer == "load": + reader = self.open_reader() + contents = JSONFile.load(reader) + reader.close() + + return contents + elif serializer == "dump": + writer = self.open_writer() + + JSONFile.dump(kwargs["contents"], writer) + + writer.close() + + return 0; + +class Entry: + """ + Class representing a JSON Entry + """ + + def __init__(self, name:str, value:Union[Any]): + self.name : str = name; + self.value : Union[Any] = value; + + async def browse(self, name:str): + """ + Browse entry and get a new Entry object if it finds something successfully + """ + + return Entry(name, self.value[name]) \ No newline at end of file diff --git a/discord/utils.py b/discord/utils.py index 4360b77a..586896e7 100644 --- a/discord/utils.py +++ b/discord/utils.py @@ -60,6 +60,8 @@ import re import sys import types import warnings +from .embeds import Embed +from .colors import Colour from .errors import InvalidArgument @@ -168,6 +170,41 @@ class CachedSlotProperty(Generic[T, T_co]): setattr(instance, self.name, value) return value +def generate_embed(header:str, content:str, footer:str, color=None): # Courtesy of Dank HadocK, my friend who coded this for my rewrite. + """ + Easy way to form embeds + + This was made by Dank Had0cK, a valuable contributor on my fork. Thanks. + + ===== + + header `str`: + + Header of your embed + + content `str`: + + Content of your embed + + footer `str`: + + Footer of your embed + + color `str` optional: + + If it is None, color will default to 2F3136 + Hexstring of your color, uses color converter by Arkae to convert hex to int. + """ + + embed = Embed() + embed.title = header + embed.description = content + if color is None: + embed.color = Colour.from_base("0x2f3136") + else: + embed.color = Colour.from_base(color, 16) + embed.set_footer(text=footer) + return embed class classproperty(Generic[T_co]): def __init__(self, fget: Callable[[Any], T_co]) -> None: