diff options
author | 2019-09-10 18:49:30 -0400 | |
---|---|---|
committer | 2019-09-10 18:49:30 -0400 | |
commit | 77c9b0f2194072b19616bad4ce81f72ebcdad27d (patch) | |
tree | 1c0efc3f8315617ef7dfdb24d62b5df21e3b29d6 | |
parent | Docstring linting chunk 3 (diff) |
Docstring linting chunk 4
-rw-r--r-- | bot/cogs/bot.py | 71 | ||||
-rw-r--r-- | bot/cogs/doc.py | 133 | ||||
-rw-r--r-- | bot/cogs/error_handler.py | 6 | ||||
-rw-r--r-- | bot/cogs/eval.py | 22 | ||||
-rw-r--r-- | bot/cogs/jams.py | 13 | ||||
-rw-r--r-- | bot/cogs/site.py | 28 | ||||
-rw-r--r-- | bot/cogs/utils.py | 28 |
7 files changed, 125 insertions, 176 deletions
diff --git a/bot/cogs/bot.py b/bot/cogs/bot.py index 828e2514c..be7922ef9 100644 --- a/bot/cogs/bot.py +++ b/bot/cogs/bot.py @@ -2,6 +2,7 @@ import ast import logging import re import time +from typing import Optional, Tuple, Union from discord import Embed, Message, RawMessageUpdateEvent from discord.ext.commands import Bot, Context, command, group @@ -17,9 +18,7 @@ log = logging.getLogger(__name__) class Bot: - """ - Bot information commands - """ + """Bot information commands.""" def __init__(self, bot: Bot): self.bot = bot @@ -46,20 +45,14 @@ class Bot: @group(invoke_without_command=True, name="bot", hidden=True) @with_role(Roles.verified) - async def bot_group(self, ctx: Context): - """ - Bot informational commands - """ - + async def bot_group(self, ctx: Context) -> None: + """Bot informational commands.""" await ctx.invoke(self.bot.get_command("help"), "bot") @bot_group.command(name='about', aliases=('info',), hidden=True) @with_role(Roles.verified) - async def about_command(self, ctx: Context): - """ - Get information about the bot - """ - + async def about_command(self, ctx: Context) -> None: + """Get information about the bot.""" embed = Embed( description="A utility bot designed just for the Python server! Try `!help` for more info.", url="https://gitlab.com/discord-python/projects/bot" @@ -77,24 +70,18 @@ class Bot: @command(name='echo', aliases=('print',)) @with_role(*MODERATION_ROLES) - async def echo_command(self, ctx: Context, *, text: str): - """ - Send the input verbatim to the current channel - """ - + async def echo_command(self, ctx: Context, *, text: str) -> None: + """Send the input verbatim to the current channel.""" await ctx.send(text) @command(name='embed') @with_role(*MODERATION_ROLES) - async def embed_command(self, ctx: Context, *, text: str): - """ - Send the input within an embed to the current channel - """ - + async def embed_command(self, ctx: Context, *, text: str) -> None: + """Send the input within an embed to the current channel.""" embed = Embed(description=text) await ctx.send(embed=embed) - def codeblock_stripping(self, msg: str, bad_ticks: bool): + def codeblock_stripping(self, msg: str, bad_ticks: bool) -> Union[Tuple[Tuple[str, Optional[str]], str], None]: """ Strip msg in order to find Python code. @@ -163,15 +150,10 @@ class Bot: log.trace(f"Returning message.\n\n{content}\n\n") return (content,), repl_code - def fix_indentation(self, msg: str): - """ - Attempts to fix badly indented code. - """ - - def unindent(code, skip_spaces=0): - """ - Unindents all code down to the number of spaces given ins skip_spaces - """ + def fix_indentation(self, msg: str) -> str: + """Attempts to fix badly indented code.""" + def unindent(code, skip_spaces: int = 0) -> str: + """Unindents all code down to the number of spaces given in skip_spaces.""" final = "" current = code[0] leading_spaces = 0 @@ -207,11 +189,13 @@ class Bot: msg = f"{first_line}\n{unindent(code, 4)}" return msg - def repl_stripping(self, msg: str): + def repl_stripping(self, msg: str) -> Tuple[str, bool]: """ Strip msg in order to extract Python code out of REPL output. Tries to strip out REPL Python code out of msg and returns the stripped msg. + + Returns a second boolean output if REPL code was found in the input msg. """ final = "" for line in msg.splitlines(keepends=True): @@ -225,7 +209,8 @@ class Bot: log.trace(f"Found REPL code in \n\n{msg}\n\n") return final.rstrip(), True - def has_bad_ticks(self, msg: Message): + def has_bad_ticks(self, msg: Message) -> bool: + """Check to see if msg contains ticks that aren't '`'.""" not_backticks = [ "'''", '"""', "\u00b4\u00b4\u00b4", "\u2018\u2018\u2018", "\u2019\u2019\u2019", "\u2032\u2032\u2032", "\u201c\u201c\u201c", "\u201d\u201d\u201d", "\u2033\u2033\u2033", @@ -234,13 +219,13 @@ class Bot: return msg.content[:3] in not_backticks - async def on_message(self, msg: Message): - """ - Detect poorly formatted Python code and send the user - a helpful message explaining how to do properly - formatted Python syntax highlighting codeblocks. + async def on_message(self, msg: Message) -> None: """ + Detect poorly formatted Python code in new messages. + If poorly formatted code is detected, send the user a helpful message explaining how to do + properly formatted Python syntax highlighting codeblocks. + """ parse_codeblock = ( ( msg.channel.id in self.channel_cooldowns @@ -355,7 +340,8 @@ class Bot: f"The message that was posted was:\n\n{msg.content}\n\n" ) - async def on_raw_message_edit(self, payload: RawMessageUpdateEvent): + async def on_raw_message_edit(self, payload: RawMessageUpdateEvent) -> None: + """Check to see if an edited message (previously called out) still contains poorly formatted code.""" if ( # Checks to see if the message was called out by the bot payload.message_id not in self.codeblock_message_ids @@ -381,6 +367,7 @@ class Bot: log.trace("User's incorrect code block has been fixed. Removing bot formatting message.") -def setup(bot): +def setup(bot: Bot) -> None: + """Bot cog load.""" bot.add_cog(Bot(bot)) log.info("Cog loaded: Bot") diff --git a/bot/cogs/doc.py b/bot/cogs/doc.py index aa49b0c25..ef14d1797 100644 --- a/bot/cogs/doc.py +++ b/bot/cogs/doc.py @@ -4,10 +4,11 @@ import logging import re import textwrap from collections import OrderedDict -from typing import Optional, Tuple +from typing import Callable, Optional, Tuple import discord from bs4 import BeautifulSoup +from bs4.element import PageElement from discord.ext import commands from markdownify import MarkdownConverter from requests import ConnectionError @@ -27,24 +28,22 @@ UNWANTED_SIGNATURE_SYMBOLS = ('[source]', '¶') WHITESPACE_AFTER_NEWLINES_RE = re.compile(r"(?<=\n\n)(\s+)") -def async_cache(max_size=128, arg_offset=0): +def async_cache(max_size: int = 128, arg_offset: int = 0) -> Callable: """ LRU cache implementation for coroutines. - :param max_size: - Specifies the maximum size the cache should have. - Once it exceeds the maximum size, keys are deleted in FIFO order. - :param arg_offset: - The offset that should be applied to the coroutine's arguments - when creating the cache key. Defaults to `0`. - """ + Once the cache exceeds the maximum size, keys are deleted in FIFO order. + An offset may be optionally provided to be applied to the coroutine's arguments when creating the cache key. + """ # Assign the cache to the function itself so we can clear it from outside. async_cache.cache = OrderedDict() - def decorator(function): + def decorator(function: Callable) -> Callable: + """Define the async_cache decorator.""" @functools.wraps(function) - async def wrapper(*args): + async def wrapper(*args) -> OrderedDict: + """Decorator wrapper for the caching logic.""" key = ':'.join(args[arg_offset:]) value = async_cache.cache.get(key) @@ -59,27 +58,25 @@ def async_cache(max_size=128, arg_offset=0): class DocMarkdownConverter(MarkdownConverter): - def convert_code(self, el, text): - """Undo `markdownify`s underscore escaping.""" + """Subclass markdownify's MarkdownCoverter to provide custom conversion methods.""" + def convert_code(self, el: PageElement, text: str) -> str: + """Undo `markdownify`s underscore escaping.""" return f"`{text}`".replace('\\', '') - def convert_pre(self, el, text): + def convert_pre(self, el: PageElement, text: str) -> str: """Wrap any codeblocks in `py` for syntax highlighting.""" - code = ''.join(el.strings) return f"```py\n{code}```" -def markdownify(html): +def markdownify(html: str) -> DocMarkdownConverter: + """Create a DocMarkdownConverter object from the input html.""" return DocMarkdownConverter(bullets='•').convert(html) class DummyObject(object): - """ - A dummy object which supports assigning anything, - which the builtin `object()` does not support normally. - """ + """A dummy object which supports assigning anything, which the builtin `object()` does not support normally.""" class SphinxConfiguration: @@ -94,14 +91,15 @@ class InventoryURL(commands.Converter): """ Represents an Intersphinx inventory URL. - This converter checks whether intersphinx - accepts the given inventory URL, and raises + This converter checks whether intersphinx accepts the given inventory URL, and raises `BadArgument` if that is not the case. + Otherwise, it simply passes through the given URL. """ @staticmethod - async def convert(ctx, url: str): + async def convert(ctx: commands.Context, url: str) -> str: + """Convert url to Intersphinx inventory URL.""" try: intersphinx.fetch_inventory(SphinxConfiguration(), '', url) except AttributeError: @@ -121,30 +119,32 @@ class InventoryURL(commands.Converter): class Doc: - def __init__(self, bot): + """A set of commands for querying & displaying documentation.""" + + def __init__(self, bot: commands.Bot): self.base_urls = {} self.bot = bot self.inventories = {} - async def on_ready(self): + async def on_ready(self) -> None: + """Refresh documentation inventory.""" await self.refresh_inventory() async def update_single( self, package_name: str, base_url: str, inventory_url: str, config: SphinxConfiguration - ): + ) -> None: """ Rebuild the inventory for a single package. - :param package_name: The package name to use, appears in the log. - :param base_url: The root documentation URL for the specified package. - Used to build absolute paths that link to specific symbols. - :param inventory_url: The absolute URL to the intersphinx inventory. - Fetched by running `intersphinx.fetch_inventory` in an - executor on the bot's event loop. - :param config: A `SphinxConfiguration` instance to mock the regular sphinx - project layout. Required for use with intersphinx. + Where: + * `package_name` is the package name to use, appears in the log + * `base_url` is the root documentation URL for the specified package, used to build + absolute paths that link to specific symbols + * `inventory_url` is the absolute URL to the intersphinx inventory, fetched by running + `intersphinx.fetch_inventory` in an executor on the bot's event loop + * `config` is a `SphinxConfiguration` instance to mock the regular sphinx + project layout, required for use with intersphinx """ - self.base_urls[package_name] = base_url fetch_func = functools.partial(intersphinx.fetch_inventory, config, '', inventory_url) @@ -158,7 +158,8 @@ class Doc: log.trace(f"Fetched inventory for {package_name}.") - async def refresh_inventory(self): + async def refresh_inventory(self) -> None: + """Refresh internal documentation inventory.""" log.debug("Refreshing documentation inventory...") # Clear the old base URLS and inventories to ensure @@ -185,16 +186,13 @@ class Doc: """ Given a Python symbol, return its signature and description. - :param symbol: The symbol for which HTML data should be returned. - :return: - A tuple in the form (str, str), or `None`. - The first tuple element is the signature of the given - symbol as a markup-free string, and the second tuple - element is the description of the given symbol with HTML - markup included. If the given symbol could not be found, - returns `None`. - """ + Returns a tuple in the form (str, str), or `None`. + The first tuple element is the signature of the given symbol as a markup-free string, and + the second tuple element is the description of the given symbol with HTML markup included. + + If the given symbol could not be found, returns `None`. + """ url = self.inventories.get(symbol) if url is None: return None @@ -222,16 +220,10 @@ class Doc: @async_cache(arg_offset=1) async def get_symbol_embed(self, symbol: str) -> Optional[discord.Embed]: """ - Using `get_symbol_html`, attempt to scrape and - fetch the data for the given `symbol`, and build - a formatted embed out of its contents. - - :param symbol: The symbol for which the embed should be returned - :return: - If the symbol is known, an Embed with documentation about it. - Otherwise, `None`. - """ + Attempt to scrape and fetch the data for the given `symbol`, and build an embed from its contents. + If the symbol is known, an Embed with documentation about it is returned. + """ scraped_html = await self.get_symbol_html(symbol) if scraped_html is None: return None @@ -266,20 +258,16 @@ class Doc: ) @commands.group(name='docs', aliases=('doc', 'd'), invoke_without_command=True) - async def docs_group(self, ctx, symbol: commands.clean_content = None): + async def docs_group(self, ctx: commands.Context, symbol: commands.clean_content = None) -> None: """Lookup documentation for Python symbols.""" - await ctx.invoke(self.get_command) @docs_group.command(name='get', aliases=('g',)) - async def get_command(self, ctx, symbol: commands.clean_content = None): + async def get_command(self, ctx: commands.Context, symbol: commands.clean_content = None) -> None: """ Return a documentation embed for a given symbol. - If no symbol is given, return a list of all available inventories. - :param ctx: Discord message context - :param symbol: The symbol for which documentation should be returned, - or nothing to get a list of all inventories + If no symbol is given, return a list of all available inventories. Examples: !docs @@ -287,7 +275,6 @@ class Doc: !docs aiohttp.ClientSession !docs get aiohttp.ClientSession """ - if symbol is None: inventory_embed = discord.Embed( title=f"All inventories (`{len(self.base_urls)}` total)", @@ -321,18 +308,13 @@ class Doc: @docs_group.command(name='set', aliases=('s',)) @with_role(*MODERATION_ROLES) async def set_command( - self, ctx, package_name: ValidPythonIdentifier, + self, ctx: commands.Context, package_name: ValidPythonIdentifier, base_url: ValidURL, inventory_url: InventoryURL - ): + ) -> None: """ Adds a new documentation metadata object to the site's database. - The database will update the object, should an existing item - with the specified `package_name` already exist. - :param ctx: Discord message context - :param package_name: The package name, for example `aiohttp`. - :param base_url: The package documentation's root URL, used to build absolute links. - :param inventory_url: The intersphinx inventory URL. + The database will update the object, should an existing item with the specified `package_name` already exist. Example: !docs set \ @@ -340,7 +322,6 @@ class Doc: https://discordpy.readthedocs.io/en/rewrite/ \ https://discordpy.readthedocs.io/en/rewrite/objects.inv """ - body = { 'package': package_name, 'base_url': base_url, @@ -364,17 +345,13 @@ class Doc: @docs_group.command(name='delete', aliases=('remove', 'rm', 'd')) @with_role(*MODERATION_ROLES) - async def delete_command(self, ctx, package_name: ValidPythonIdentifier): + async def delete_command(self, ctx: commands.Context, package_name: ValidPythonIdentifier) -> None: """ Removes the specified package from the database. - :param ctx: Discord message context - :param package_name: The package name, for example `aiohttp`. - Examples: !docs delete aiohttp """ - await self.bot.api_client.delete(f'bot/documentation-links/{package_name}') async with ctx.typing(): @@ -384,5 +361,7 @@ class Doc: await ctx.send(f"Successfully deleted `{package_name}` and refreshed inventory.") -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Doc cog load.""" bot.add_cog(Doc(bot)) + log.info("Cog loaded: Doc") diff --git a/bot/cogs/error_handler.py b/bot/cogs/error_handler.py index 2063df09d..f8a27fda8 100644 --- a/bot/cogs/error_handler.py +++ b/bot/cogs/error_handler.py @@ -24,7 +24,8 @@ class ErrorHandler: def __init__(self, bot: Bot): self.bot = bot - async def on_command_error(self, ctx: Context, e: CommandError): + async def on_command_error(self, ctx: Context, e: CommandError) -> None: + """Provide command error handling.""" command = ctx.command parent = None @@ -87,6 +88,7 @@ class ErrorHandler: raise e -def setup(bot: Bot): +def setup(bot: Bot) -> None: + """Error handler cog load.""" bot.add_cog(ErrorHandler(bot)) log.info("Cog loaded: Events") diff --git a/bot/cogs/eval.py b/bot/cogs/eval.py index 8e97a35a2..5fbd9dca5 100644 --- a/bot/cogs/eval.py +++ b/bot/cogs/eval.py @@ -6,6 +6,7 @@ import re import textwrap import traceback from io import StringIO +from typing import Any, Tuple, Union import discord from discord.ext.commands import Bot, group @@ -18,10 +19,7 @@ log = logging.getLogger(__name__) class CodeEval: - """ - Owner and admin feature that evaluates code - and returns the result to the channel. - """ + """Owner and admin feature that evaluates code and returns the result to the channel.""" def __init__(self, bot: Bot): self.bot = bot @@ -31,7 +29,8 @@ class CodeEval: self.interpreter = Interpreter(bot) - def _format(self, inp, out): # (str, Any) -> (str, discord.Embed) + def _format(self, inp: str, out: Any) -> Tuple[str, Union[discord.embed, None]]: + """Format the eval output into a string & attempt to format it into an Embed.""" self._ = out res = "" @@ -124,7 +123,8 @@ class CodeEval: return res # Return (text, embed) - async def _eval(self, ctx, code): # (discord.Context, str) -> None + async def _eval(self, ctx: discord.Context, code: str) -> None: + """Eval the input code string & send an embed to the invoking context.""" self.ln += 1 if code.startswith("exit"): @@ -174,16 +174,15 @@ async def func(): # (None,) -> Any @group(name='internal', aliases=('int',)) @with_role(Roles.owner, Roles.admin) - async def internal_group(self, ctx): + async def internal_group(self, ctx: discord.Context) -> None: """Internal commands. Top secret!""" - if not ctx.invoked_subcommand: await ctx.invoke(self.bot.get_command("help"), "internal") @internal_group.command(name='eval', aliases=('e',)) @with_role(Roles.admin, Roles.owner) - async def eval(self, ctx, *, code: str): - """ Run eval in a REPL-like format. """ + async def eval(self, ctx: discord.Context, *, code: str) -> None: + """Run eval in a REPL-like format.""" code = code.strip("`") if re.match('py(thon)?\n', code): code = "\n".join(code.split("\n")[1:]) @@ -197,6 +196,7 @@ async def func(): # (None,) -> Any await self._eval(ctx, code) -def setup(bot): +def setup(bot: Bot) -> None: + """Code eval cog load.""" bot.add_cog(CodeEval(bot)) log.info("Cog loaded: Eval") diff --git a/bot/cogs/jams.py b/bot/cogs/jams.py index 96b98e559..f7a5896c0 100644 --- a/bot/cogs/jams.py +++ b/bot/cogs/jams.py @@ -10,9 +10,7 @@ log = logging.getLogger(__name__) class CodeJams: - """ - Manages the code-jam related parts of our server - """ + """Manages the code-jam related parts of our server.""" def __init__(self, bot: commands.Bot): self.bot = bot @@ -22,14 +20,12 @@ class CodeJams: async def createteam( self, ctx: commands.Context, team_name: str, members: commands.Greedy[Member] - ): + ) -> None: """ - Create a team channel (both voice and text) in the Code Jams category, assign roles - and then add overwrites for the team. + Create team channels (voice and text) in the Code Jams category, assign roles, and add overwrites for the team. The first user passed will always be the team leader. """ - # We had a little issue during Code Jam 4 here, the greedy converter did it's job # and ignored anything which wasn't a valid argument which left us with teams of # two members or at some times even 1 member. This fixes that by checking that there @@ -105,6 +101,7 @@ class CodeJams: await ctx.send(f":ok_hand: Team created: {team_channel.mention}") -def setup(bot): +def setup(bot: commands.Bot) -> None: + """Code Jams cog load.""" bot.add_cog(CodeJams(bot)) log.info("Cog loaded: CodeJams") diff --git a/bot/cogs/site.py b/bot/cogs/site.py index 37bf4f4ea..a941f27a7 100644 --- a/bot/cogs/site.py +++ b/bot/cogs/site.py @@ -19,15 +19,13 @@ class Site: self.bot = bot @group(name="site", aliases=("s",), invoke_without_command=True) - async def site_group(self, ctx): + async def site_group(self, ctx: Context) -> None: """Commands for getting info about our website.""" - await ctx.invoke(self.bot.get_command("help"), "site") @site_group.command(name="home", aliases=("about",)) - async def site_main(self, ctx: Context): + async def site_main(self, ctx: Context) -> None: """Info about the website itself.""" - url = f"{URLs.site_schema}{URLs.site}/" embed = Embed(title="Python Discord website") @@ -43,9 +41,8 @@ class Site: await ctx.send(embed=embed) @site_group.command(name="resources") - async def site_resources(self, ctx: Context): + async def site_resources(self, ctx: Context) -> None: """Info about the site's Resources page.""" - url = f"{INFO_URL}/resources" embed = Embed(title="Resources") @@ -60,9 +57,8 @@ class Site: await ctx.send(embed=embed) @site_group.command(name="help") - async def site_help(self, ctx: Context): + async def site_help(self, ctx: Context) -> None: """Info about the site's Getting Help page.""" - url = f"{INFO_URL}/help" embed = Embed(title="Getting Help") @@ -77,9 +73,8 @@ class Site: await ctx.send(embed=embed) @site_group.command(name="faq") - async def site_faq(self, ctx: Context): + async def site_faq(self, ctx: Context) -> None: """Info about the site's FAQ page.""" - url = f"{INFO_URL}/faq" embed = Embed(title="FAQ") @@ -96,14 +91,8 @@ class Site: @site_group.command(aliases=['r', 'rule'], name='rules') @redirect_output(destination_channel=Channels.bot, bypass_roles=STAFF_ROLES) - async def site_rules(self, ctx: Context, *rules: int): - """ - Provides a link to the `rules` endpoint of the website, or displays - specific rules, if they are requested. - - **`ctx`:** The Discord message context - **`rules`:** The rules a user wants to get. - """ + async def site_rules(self, ctx: Context, *rules: int) -> None: + """Provides a link to the `rules` endpoint of the website, or displays specific rule(s), if requested.""" rules_embed = Embed(title='Rules', color=Colour.blurple()) rules_embed.url = f"{URLs.site_schema}{URLs.site}/about/rules" @@ -135,6 +124,7 @@ class Site: await LinePaginator.paginate(final_rules, ctx, rules_embed, max_lines=3) -def setup(bot): +def setup(bot: Bot) -> None: + """Site cog load.""" bot.add_cog(Site(bot)) log.info("Cog loaded: Site") diff --git a/bot/cogs/utils.py b/bot/cogs/utils.py index 0c6d9d2ba..09e8f70d6 100644 --- a/bot/cogs/utils.py +++ b/bot/cogs/utils.py @@ -6,7 +6,7 @@ from email.parser import HeaderParser from io import StringIO from discord import Colour, Embed -from discord.ext.commands import AutoShardedBot, Context, command +from discord.ext.commands import Bot, CheckFailure, Context, command from bot.constants import Channels, NEGATIVE_REPLIES, STAFF_ROLES from bot.decorators import InChannelCheckFailure, in_channel @@ -15,22 +15,17 @@ log = logging.getLogger(__name__) class Utils: - """ - A selection of utilities which don't have a clear category. - """ + """A selection of utilities which don't have a clear category.""" - def __init__(self, bot: AutoShardedBot): + def __init__(self, bot: Bot): self.bot = bot self.base_pep_url = "http://www.python.org/dev/peps/pep-" self.base_github_pep_url = "https://raw.githubusercontent.com/python/peps/master/pep-" @command(name='pep', aliases=('get_pep', 'p')) - async def pep_command(self, ctx: Context, pep_number: str): - """ - Fetches information about a PEP and sends it to the channel. - """ - + async def pep_command(self, ctx: Context, pep_number: str) -> None: + """Fetches information about a PEP and sends it to the channel.""" if pep_number.isdigit(): pep_number = int(pep_number) else: @@ -90,11 +85,8 @@ class Utils: @command() @in_channel(Channels.bot, bypass_roles=STAFF_ROLES) - async def charinfo(self, ctx, *, characters: str): - """ - Shows you information on up to 25 unicode characters. - """ - + async def charinfo(self, ctx: Context, *, characters: str) -> None: + """Shows you information on up to 25 unicode characters.""" match = re.match(r"<(a?):(\w+):(\d+)>", characters) if match: embed = Embed( @@ -133,7 +125,8 @@ class Utils: await ctx.send(embed=embed) - async def __error(self, ctx, error): + async def __error(self, ctx: Context, error: CheckFailure) -> None: + """Send Check failure error to invoking context if command is invoked in a blacklisted channel by non-staff.""" embed = Embed(colour=Colour.red()) if isinstance(error, InChannelCheckFailure): embed.title = random.choice(NEGATIVE_REPLIES) @@ -141,6 +134,7 @@ class Utils: await ctx.send(embed=embed) -def setup(bot): +def setup(bot: Bot) -> None: + """Utils cog load.""" bot.add_cog(Utils(bot)) log.info("Cog loaded: Utils") |