diff --git a/.vscode/settings.json b/.vscode/settings.json index a8ea379..12a6ab6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "python.pythonPath": "d:\\Python\\BDBF\\venv\\Scripts\\python.exe" + "python.pythonPath": "d:\\Python\\BDBF\\venv\\Scripts\\python.exe", + "python.linting.enabled": true, + "python.formatting.provider": "autopep8", + "python.linting.pycodestyleEnabled": true } \ No newline at end of file diff --git a/bdbf/__init__.py b/bdbf/__init__.py index 1e2ba63..3073cb7 100644 --- a/bdbf/__init__.py +++ b/bdbf/__init__.py @@ -8,4 +8,4 @@ __author__ = 'Bertik23' __license__ = 'MIT' __copyright__ = 'Copyright 2020-2021 Bertik23' -__version__ = '1.1.1' \ No newline at end of file +__version__ = '1.1.2' \ No newline at end of file diff --git a/bdbf/functions.py b/bdbf/functions.py index a89ff4e..739cc45 100644 --- a/bdbf/functions.py +++ b/bdbf/functions.py @@ -7,7 +7,8 @@ last10messages: dict = {} -def IntToRgb(RGBint: int):# -> typing.Tuple[int,int,int]: + +def IntToRgb(RGBint: int): # -> typing.Tuple[int,int,int]: """Converts a integer color value to a RGB tuple :param RGBint: :class:`int` @@ -16,12 +17,13 @@ def IntToRgb(RGBint: int):# -> typing.Tuple[int,int,int]: :returns: :class:`tuple[int,int,int]` RGB tuple """ - blue = RGBint & 255 + blue = RGBint & 255 green = (RGBint >> 8) & 255 - red = (RGBint >> 16) & 255 + red = (RGBint >> 16) & 255 return red, green, blue -def RgbToInt(rgb: typing.Tuple[int,int,int]):# -> int: + +def RgbToInt(rgb: typing.Tuple[int, int, int]): # -> int: """Converts a RGB tuple to a integer color value :param rgb: :class:`tuple[int,int,int]` @@ -35,29 +37,44 @@ def RgbToInt(rgb: typing.Tuple[int,int,int]):# -> int: red = rgb[0] green = rgb[1] blue = rgb[2] - RGBint = (red<<16) + (green<<8) + blue + RGBint = (red << 16) + (green << 8) + blue return RGBint -def hasLink(text: str):# -> bool: + +def hasLink(text: str): # -> bool: """Returns if a string contains a link. :param text: :class:`str` String to check - + :returns: :class:`bool` If the string has a link. """ regex = re.compile( - "(([\w]+:)?//)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,63}(:[\d]+)?(/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?" + r"(([\w]+:)?//)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})" + r"+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,63}(:[\d]+)?(/([-+_~.\d" + r"\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?" + r"(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?" ) if regex.match(text): return True else: return False -def embed(title, url = None, description = None, fields = None, image = None, thumbnail = None, author = None, footer=None, color: typing.Union[typing.Tuple[int,int,int], int] = 0):# -> discord.Embed: + +def embed( + title, + url=discord.embeds.EmptyEmbed, + description=discord.embeds.EmptyEmbed, + fields=None, + image=None, + thumbnail=None, + author=None, + footer=None, + color: typing.Union[typing.Tuple[int, int, int], int] = 0 +): # -> discord.Embed: """Returns discord embed from given parameters - + :param title: :class:`str` Title of the embed :param url: :class:`Optional[str]` @@ -66,7 +83,8 @@ def embed(title, url = None, description = None, fields = None, image = None, th Description of the embed :param fields: :class:`Optional[List[Tuple[str,str,Optional[bool]]]]` Fields of the embed. - A tuple with item 1 being the name of the field, item 2 the value and item 3 weather is inline or not, item 3 is optional + A tuple with item 1 being the name of the field, item 2 the value and + item 3 weather is inline or not, item 3 is optional :param image: :class:`Optional[str]` Url of the embed image :param thumbnail: :class:`Optional[str]` @@ -77,35 +95,40 @@ def embed(title, url = None, description = None, fields = None, image = None, th Footer of the embed :param color: :class:`Optional[Union[Tuple[int,int,int],int]]` Color of the embed, eighter RGB tuple or int - + :returns: discord.Embed""" if type(color) == tuple: color = RgbToInt(color) - if fields == None: + if fields is None: fields = [] fieldKeys = [] - for i,f in enumerate(fields): + for i, f in enumerate(fields): if len(f) == 2: - fieldKeys.append(("name","value")) + fieldKeys.append(("name", "value")) elif len(f) == 3: - fieldKeys.append(("name","value","inline")) + fieldKeys.append(("name", "value", "inline")) - #print(fieldKeys, fields, [list(i) for i in [zip(j,fields[k]) for k,j in enumerate(fieldKeys)]]) - fields = [dict(l) for l in [list(i) for i in [zip(j,fields[k]) for k,j in enumerate(fieldKeys)]]] - #print(fields) + fields = [ + dict(a) for a in [ + list(i) for i in [ + zip(j, fields[k]) for k, j in enumerate(fieldKeys) + ] + ] + ] + # print(fields) e = discord.Embed.from_dict({ - "title": title, - "color": color, - "description": description, - "image": image, - "thumbnail": thumbnail, - "author": author, - "fields": fields, - "url": url, - "footer": footer - } - ) + "title": title, + "color": color, + "description": description, + "image": image, + "thumbnail": thumbnail, + "author": author, + "fields": fields, + "url": url, + "footer": footer + } + ) return e diff --git a/bdbf/main.py b/bdbf/main.py index 15fa3b3..516f579 100644 --- a/bdbf/main.py +++ b/bdbf/main.py @@ -8,10 +8,12 @@ log = logging.getLogger(__name__) + class Client(discord.Client): r"""Discord client class inherited from discord.Client. This documentation covers only the changes. For the inherited - functions please head to the `discord.py documentation `_ + functions please head to the `discord.py documentation + `_ :param embedFooter: Optional[:class:`dict`] The footer of embeds. @@ -45,11 +47,12 @@ class Client(discord.Client): If sending exceptions to discord is enabled. Default: True """ + def __init__(self, *, loop=None, **options): super().__init__(loop=loop, **options) self.commands = {} self.embedFooter = options.pop("embedFooter", {}) - self.embedColor = options.pop("embedColor", (0,0,0)) + self.embedColor = options.pop("embedColor", (0, 0, 0)) self.botName = options.pop("botName", None) self.commandPrefix = options.pop("commandPrefix", None) self.logging = options.pop("logging", False) @@ -65,7 +68,15 @@ def __init__(self, *, loop=None, **options): @self.command("help") async def help(msg, *args): """Help""" - fields = [(command, self.commands[command].__doc__ if self.commands[command].__doc__ != None else f"{command} help", options.pop("isHelpInline", True)) for command in self.commands.keys()] + fields = [ + ( + command, + self.commands[command].__doc__ + if self.commands[command].__doc__ is not None + else f"{command} help", + options.pop("isHelpInline", True) + ) for command in self.commands.keys() + ] e = self.embed(f"Help for {self.botName}", fields=fields) await msg.channel.send(embed=e) @@ -76,21 +87,25 @@ async def on_raw_reaction_add(payload): @self.event async def on_raw_reaction_remove(payload): pass - + def command(self, commandName, **options): r"""Wrapper fuction for making commands. :param worksOnlyInGuilds: Optional[:class:`List[int]`] - List of guild where the command will work. List of guild where the command will work. + List of guild where the command will work. List of guild where the + command will work. :param worksOnlyInChannels: Optional[:class:`List[int]`] - List of channels where the command will work. If not provided will for in all. + List of channels where the command will work. If not provided will + for in all. :param doesntWorkInGuilds: Optional[:class:`List[int]`] - List of guilds where the command wont work. List of guild where the command will work. + List of guilds where the command wont work. List of guild where + the command will work. :param doesntWorkInChannels: Optional[:class:`List[int]`] - List of guilds where the command wont work. List of guild where the command will work. + List of guilds where the command wont work. List of guild where + the command will work. Example ------- @@ -99,8 +114,9 @@ def command(self, commandName, **options): def command(message): print(message.content)""" def register(function): - if function.__doc__ != None: - function.__doc__ = function.__doc__.replace("%commandPrefix%", self.commandPrefix) + if function.__doc__ is not None: + function.__doc__ = function.__doc__.replace( + "%commandPrefix%", self.commandPrefix) for option in options: setattr(function, option, options[option]) if not self.caseSensitiveCommands: @@ -108,9 +124,15 @@ def register(function): else: name = commandName if name in self.commands.keys(): - raise CommandNameAlreadyRegistered(f"The command name {name} is already registered and can't be used again.") + raise CommandNameAlreadyRegistered( + f"The command name {name} is already" + "registered and can't be used again." + ) self.commands[name] = function - log.debug(f"Function {function.__name__} has been registered for command {name}") + log.debug( + f"Function {function.__name__}" + f"has been registered for command {name}" + ) return function return register @@ -120,17 +142,21 @@ async def tryCommand(self, msg, command, *options): e = "" try: if command in self.commands.keys(): - if hasattr(self.commands[command],"worksOnlyInGuilds"): - if msg.channel.guild.id in self.commands[command].worksOnlyInGuilds: + if hasattr(self.commands[command], "worksOnlyInGuilds"): + if msg.channel.guild.id in self.commands[ + command].worksOnlyInGuilds: await self.commands[command](msg, *options) - elif hasattr(self.commands[command],"worksOnlyInChannels"): - if msg.channel.id in self.commands[command].worksOnlyInChannels: + elif hasattr(self.commands[command], "worksOnlyInChannels"): + if msg.channel.id in self.commands[ + command].worksOnlyInChannels: await self.commands[command](msg, *options) elif hasattr(self.commands[command], "doesntWorkInGuilds"): - if msg.channel.guild.id not in self.commands[command].doesntWorkInGuilds: + if msg.channel.guild.id not in self.commands[ + command].doesntWorkInGuilds: await self.commands[command](msg, *options) elif hasattr(self.commands[command], "doesntWorkInChannels"): - if msg.channel.id not in self.commands[command].doesntWorkInChannels: + if msg.channel.id not in self.commands[ + command].doesntWorkInChannels: await self.commands[command](msg, *options) else: await self.commands[command](msg, *options) @@ -142,12 +168,13 @@ async def tryCommand(self, msg, command, *options): e = traceback.format_exc() if self.sendExceptions: await msg.channel.send(f"```{e}```"[:2000]) - if self.logging and command != None: - if self.commandLoggingFunction != None: + if self.logging and command is not None: + if self.commandLoggingFunction is not None: if asyncio.iscoroutinefunction(self.commandLoggingFunction): await self.commandLoggingFunction(msg) else: - self.commandLoggingFunction(command, msg, time.time()-startTime, e) + self.commandLoggingFunction( + command, msg, time.time()-startTime, e) async def useCommand(self, msg): message = msg.content @@ -156,15 +183,19 @@ async def useCommand(self, msg): if len(message[len(self.commandPrefix):].split(" ", 1)) == 1: cmd, args = message[len(self.commandPrefix):], None else: - cmd, args = message[len(self.commandPrefix):].split(" ", 1)[0], message[1:].split(" ",1)[1] + cmd, args = message[ + len(self.commandPrefix): + ].split(" ", 1)[ + 0 + ], message[1:].split(" ", 1)[1] else: cmd, args = None, None else: cmd, args = None, None - if not self.caseSensitiveCommands and cmd != None: + if not self.caseSensitiveCommands and cmd is not None: cmd = cmd.lower() - + if not self.createTaskCommands: await self.tryCommand(msg, cmd, args) else: @@ -173,9 +204,11 @@ async def useCommand(self, msg): def event(self, coro): r"""A decorator that registers an event to listen to. - You can find more info about the events on the :ref:`documentation below `. + You can find more info about the events on the :ref:`documentation + below `. - The events must be a :ref:`coroutine `, if not, :exc:`TypeError` is raised. + The events must be a :ref:`coroutine `, if not, + :exc:`TypeError` is raised. Example --------- @@ -194,12 +227,12 @@ async def on_ready(): if coro.__name__ == "on_message": async def on_message(msg, **options): a = await coro(msg, **options) - if a == None: + if a is None: a = {} - if a.pop("command",True): + if a.pop("command", True): await self.useCommand(msg) if a.pop("log", True) and self.logging: - if self.loggingFunction != None: + if self.loggingFunction is not None: if asyncio.iscoroutinefunction(self.loggingFunction): await self.loggingFunction(msg) else: @@ -221,23 +254,40 @@ async def on_raw_reaction_remove(payload): return a return super().event(on_raw_reaction_remove) - return super().event(coro) async def tryReactionRole(self, a, payload): if a == "add": try: - await payload.member.add_roles(discord.Object(self.roleToReaction[payload.message_id][payload.emoji.name])) + await payload.member.add_roles( + discord.Object( + self.roleToReaction[ + payload.message_id + ][ + payload.emoji.name + ] + ) + ) except KeyError: pass elif a == "remove": try: - await self.get_guild(payload.guild_id).get_member(payload.user_id).remove_roles(discord.Object(self.roleToReaction[payload.message_id][payload.emoji.name])) + await self.get_guild( + payload.guild_id + ).get_member( + payload.user_id + ).remove_roles( + discord.Object( + self.roleToReaction[ + payload.message_id + ][ + payload.emoji.name + ] + ) + ) except KeyError: pass - - def logMessage(self, function): r"""Wrapper fuction for making a logging function. Like :: @@ -245,9 +295,12 @@ def logMessage(self, function): @client.logMessage def log(message): print(message.content)""" - + self.loggingFunction = function - log.debug(f"Function {function.__name__} has been registered as message logging function.") + log.debug( + f"Function {function.__name__} has been " + "registered as message logging function." + ) return function def logCommand(self, function): @@ -257,16 +310,20 @@ def logCommand(self, function): @client.logCommand def log(command, message, time, state, exception): print(message.content)""" - + self.commandLoggingFunction = function - log.debug(f"Function {function.__name__} has been registered as command logging function.") + log.debug( + f"Function {function.__name__} has been " + "registered as command logging function." + ) return function def reactionRole(self, msg, emoji, role): r"""Function to add reaction role functions to a message. - + :param msg: :class:`Union[discord.Message,int]` - Message or message id of the message you want to add the reaction role functionality. + Message or message id of the message you want to add + the reaction role functionality. :param emoji: :class:`str` Emoji. If a unicode emoji use it, if a custom emoji use it's name. :param role: :class:`int` @@ -274,12 +331,15 @@ def reactionRole(self, msg, emoji, role): if type(msg) == discord.Message: msg = msg.id elif type(msg) != int: - raise TypeError(f"Message has to be eighter int or discord.Message not {type(msg)}") - + raise TypeError( + f"Message has to be eighter " + f"int or discord.Message not {type(msg)}") + self.roleToReaction[msg] = {emoji: role} def embed(self, title, **options): - """Returns discord embed from given parameters with automatic footer and color options. + """Returns discord embed from given parameters with automatic + footer and color options. :param title: :class:`str` Title of the embed @@ -289,13 +349,19 @@ def embed(self, title, **options): Description of the embed :param fields: :class:`Optional[List[Tuple[str,str,Optional[bool]]]]` Fields of the embed. - A tuple with item 1 being the name of the field, item 2 the value and item 3 weather is inline or not, item 3 is optional + A tuple with item 1 being the name of the field, item 2 the value + and item 3 weather is inline or not, item 3 is optional :param image: :class:`Optional[str]` Url of the embed image :param thumbnail: :class:`Optional[str]` Url of the thumbnail image :param author: :class:`Optional[Dict[str,str]]` Author of the embed - + :returns: :class:`discord.Embed`""" - return embed(title,footer=self.embedFooter, color=self.embedColor, **options) + return embed( + title, + footer=self.embedFooter, + color=self.embedColor, + **options + ) diff --git a/requirements.txt b/requirements.txt index 1955862..84e498a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ # BDBF/build/lib/bdbf/commands.py: 4 # BDBF/build/lib/bdbf/main.py: 1 # BDBF/tests/test.py: 8 -discord.py == 1.4.1 +discord.py == 1.7.2 # BDBF/setup.py: 1 setuptools == 49.2.0 diff --git a/tests/test.py b/tests/test.py index 3e25821..9787857 100644 --- a/tests/test.py +++ b/tests/test.py @@ -4,40 +4,67 @@ logging.basicConfig(level=logging.INFO) log = logging.getLogger(__name__) -client = bdbf.Client(commandPrefix="%", logging=True, caseSensitiveCommands=False) +client = bdbf.Client( + commandPrefix="%", + logging=True, + caseSensitiveCommands=False) print(bdbf.__version__) + @client.command("test", doesntWorkInChannels=[727212369117446252]) async def test(msg, *args): """Test""" print("ahoj", args) + @client.command("test2") async def test1(msg, *args): print("ahoj2") raise ValueError("Test") + @client.logMessage def loging(msg): log.info(msg.content) + @client.logCommand def loging(*args): log.info(args) + @client.event async def on_ready(): print("Ready") + @client.event async def on_message(msg): - #print(msg.content) + # print(msg.content) if msg.content == "0": return {"log": False} -client.reactionRole(775405122137489458,"TheBot",700424827839971499) +client.reactionRole(775405122137489458, "TheBot", 700424827839971499) + -client.embed("Ahoj",fields=[("ahoj","nice"),("nice","ahoj",True)]) +@client.command("t") +async def test_command(msg, args): + await msg.reply( + embed=client.embed( + "Ahoj", + fields=[ + ( + "ahoj", + "nice" + ), + ( + "nice", + "ahoj", + True + ) + ] + ) + ) client.run("")