diff options
author | 2018-03-02 10:45:47 +0000 | |
---|---|---|
committer | 2018-03-02 11:45:47 +0100 | |
commit | 22732d129338cfefd914c7e9c213ad540d694815 (patch) | |
tree | a4a19da7c53d1f5d513df25ae86126be17f06241 | |
parent | Adding annotations and making things more beautiful in __init__.py (diff) |
Cog management #1404e (#19)
* Some work towards cog management commands
* Constants for chevron emoji
* Cogs module should use chevron emoji
* Cog reloading command
* Small cleanup
* Cogs cog is always loaded
* Changes for Aperture
* Changes for Lemon
* Linting
* Linting
* Make lemon more comfortable
* Ability to reload all cogs
* Fix format string
* Allow reload of cog management cog
-rw-r--r-- | bot/__main__.py | 3 | ||||
-rw-r--r-- | bot/cogs/cogs.py | 270 | ||||
-rw-r--r-- | bot/constants.py | 3 |
3 files changed, 275 insertions, 1 deletions
diff --git a/bot/__main__.py b/bot/__main__.py index fa50ae220..895603689 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -29,10 +29,11 @@ bot.load_extension("bot.cogs.events") # Commands, etc bot.load_extension("bot.cogs.bot") +bot.load_extension("bot.cogs.cogs") bot.load_extension("bot.cogs.clickup") bot.load_extension("bot.cogs.deployment") -bot.load_extension("bot.cogs.fun") bot.load_extension("bot.cogs.eval") +bot.load_extension("bot.cogs.fun") bot.load_extension("bot.cogs.verification") bot.run(os.environ.get("BOT_TOKEN")) diff --git a/bot/cogs/cogs.py b/bot/cogs/cogs.py new file mode 100644 index 000000000..7ad27656e --- /dev/null +++ b/bot/cogs/cogs.py @@ -0,0 +1,270 @@ +# coding=utf-8 +import os + +from discord import ClientException, Colour, Embed +from discord.ext.commands import AutoShardedBot, Context, command + +from bot.constants import ( + ADMIN_ROLE, BOT_AVATAR_URL, DEVOPS_ROLE, GITHUB_URL_BOT, + GREEN_CHEVRON, MODERATOR_ROLE, OWNER_ROLE, RED_CHEVRON, + WHITE_CHEVRON +) +from bot.decorators import with_role +from bot.utils import paginate + + +class Cogs: + """ + Cog management commands + """ + + def __init__(self, bot: AutoShardedBot): + self.bot = bot + self.cogs = {} + + # Load up the cog names + for filename in os.listdir("bot/cogs"): + if filename.endswith(".py") and "_" not in filename: + if os.path.isfile(f"bot/cogs/{filename}"): + cog = filename[:-3] + + self.cogs[cog] = f"bot.cogs.{cog}" + + # Allow reverse lookups by reversing the pairs + self.cogs.update({v: k for k, v in self.cogs.items()}) + + @command(name="cogs.load()", aliases=["cogs.load", "load_cog"]) + @with_role(MODERATOR_ROLE, ADMIN_ROLE, OWNER_ROLE, DEVOPS_ROLE) + async def load_command(self, ctx: Context, cog: str): + """ + Load up an unloaded cog, given the module containing it + + You can specify the cog name for any cogs that are placed directly within `bot.cogs`, or specify the + entire module directly. + """ + + cog = cog.lower() + + embed = Embed() + embed.colour = Colour.red() + + embed.set_author( + name="Python Bot (Cogs)", + url=GITHUB_URL_BOT, + icon_url=BOT_AVATAR_URL + ) + + if cog in self.cogs: + full_cog = self.cogs[cog] + elif "." in cog: + full_cog = cog + else: + full_cog = None + embed.description = f"Unknown cog: {cog}" + + if full_cog not in self.bot.extensions: + try: + self.bot.load_extension(full_cog) + except ClientException: + embed.description = f"Invalid cog: {cog}\n\nCog does not have a `setup()` function" + except ImportError: + embed.description = f"Invalid cog: {cog}\n\nCould not find cog module {full_cog}" + except Exception as e: + embed.description = f"Failed to load cog: {cog}\n\n```{e}```" + else: + embed.description = f"Cog loaded: {cog}" + embed.colour = Colour.green() + else: + embed.description = f"Cog {cog} is already loaded" + + await ctx.send(embed=embed) + + @command(name="cogs.unload()", aliases=["cogs.unload", "unload_cog"]) + @with_role(MODERATOR_ROLE, ADMIN_ROLE, OWNER_ROLE, DEVOPS_ROLE) + async def unload_command(self, ctx: Context, cog: str): + """ + Unload an already-loaded cog, given the module containing it + + You can specify the cog name for any cogs that are placed directly within `bot.cogs`, or specify the + entire module directly. + """ + + cog = cog.lower() + + embed = Embed() + embed.colour = Colour.red() + + embed.set_author( + name="Python Bot (Cogs)", + url=GITHUB_URL_BOT, + icon_url=BOT_AVATAR_URL + ) + + if cog in self.cogs: + full_cog = self.cogs[cog] + elif "." in cog: + full_cog = cog + else: + full_cog = None + embed.description = f"Unknown cog: {cog}" + + if full_cog == "bot.cogs.cogs": + embed.description = "You may not unload the cog management cog!" + elif full_cog in self.bot.extensions: + try: + self.bot.unload_extension(full_cog) + except Exception as e: + embed.description = f"Failed to unload cog: {cog}\n\n```{e}```" + else: + embed.description = f"Cog unloaded: {cog}" + embed.colour = Colour.green() + else: + embed.description = f"Cog {cog} is not loaded" + + await ctx.send(embed=embed) + + @command(name="cogs.reload()", aliases=["cogs.reload", "reload_cog"]) + @with_role(MODERATOR_ROLE, ADMIN_ROLE, OWNER_ROLE, DEVOPS_ROLE) + async def reload_command(self, ctx: Context, cog: str): + """ + Reload an unloaded cog, given the module containing it + + You can specify the cog name for any cogs that are placed directly within `bot.cogs`, or specify the + entire module directly. + + If you specify "*" as the cog, every cog currently loaded will be unloaded, and then every cog present in the + bot/cogs directory will be loaded. + """ + + cog = cog.lower() + + embed = Embed() + embed.colour = Colour.red() + + embed.set_author( + name="Python Bot (Cogs)", + url=GITHUB_URL_BOT, + icon_url=BOT_AVATAR_URL + ) + + if cog == "*": + full_cog = cog + elif cog in self.cogs: + full_cog = self.cogs[cog] + elif "." in cog: + full_cog = cog + else: + full_cog = None + embed.description = f"Unknown cog: {cog}" + + if full_cog == "*": + all_cogs = [ + f"bot.cogs.{fn[:-3]}" for fn in os.listdir("bot/cogs") + if os.path.isfile(f"bot/cogs/{fn}") and fn.endswith(".py") and "_" not in fn + ] + + failed_unloads = {} + failed_loads = {} + + unloaded = 0 + loaded = 0 + + for loaded_cog in self.bot.extensions.copy().keys(): + try: + self.bot.unload_extension(loaded_cog) + except Exception as e: + failed_unloads[loaded_cog] = str(e) + else: + unloaded += 1 + + for unloaded_cog in all_cogs: + try: + self.bot.load_extension(unloaded_cog) + except Exception as e: + failed_loads[unloaded_cog] = str(e) + else: + loaded += 1 + + lines = [ + "**All cogs reloaded**", + f"**Unloaded**: {unloaded} / **Loaded**: {loaded}" + ] + + if failed_unloads: + lines.append("\n**Unload failures**") + + for cog, error in failed_unloads: + lines.append(f"`{cog}` {WHITE_CHEVRON} `{error}`") + + if failed_loads: + lines.append("\n**Load failures**") + + for cog, error in failed_loads: + lines.append(f"`{cog}` {WHITE_CHEVRON} `{error}`") + + return await paginate(lines, ctx, embed, empty=False) + + elif full_cog in self.bot.extensions: + try: + self.bot.unload_extension(full_cog) + self.bot.load_extension(full_cog) + except Exception as e: + embed.description = f"Failed to reload cog: {cog}\n\n```{e}```" + else: + embed.description = f"Cog reload: {cog}" + embed.colour = Colour.green() + else: + embed.description = f"Cog {cog} is not loaded" + + await ctx.send(embed=embed) + + @command(name="cogs.get_all()", aliases=["cogs.get_all", "get_cogs", "get_all_cogs", "cogs", "cogs.list"]) + @with_role(MODERATOR_ROLE, ADMIN_ROLE, OWNER_ROLE, DEVOPS_ROLE) + async def list_command(self, ctx: Context): + """ + Get a list of all cogs, including their loaded status. + + A red double-chevron indicates that the cog is unloaded. Green indicates that the cog is currently loaded. + """ + + embed = Embed() + lines = [] + cogs = {} + + embed.colour = Colour.blurple() + embed.set_author( + name="Python Bot (Cogs)", + url=GITHUB_URL_BOT, + icon_url=BOT_AVATAR_URL + ) + + for key, value in self.cogs.items(): + if "." not in key: + continue + + if key in self.bot.extensions: + cogs[key] = True + else: + cogs[key] = False + + for key in self.bot.extensions.keys(): + if key not in self.cogs: + cogs[key] = True + + for cog, loaded in sorted(cogs.items(), key=lambda x: x[0]): + if cog in self.cogs: + cog = self.cogs[cog] + + if loaded: + chevron = GREEN_CHEVRON + else: + chevron = RED_CHEVRON + + lines.append(f"{chevron} {cog}") + + await paginate(lines, ctx, embed, max_size=300, empty=False) + + +def setup(bot): + bot.add_cog(Cogs(bot)) + print("Cog loaded: Cogs") diff --git a/bot/constants.py b/bot/constants.py index fb4da8a49..cf42996c1 100644 --- a/bot/constants.py +++ b/bot/constants.py @@ -22,6 +22,9 @@ CLICKUP_KEY = os.environ.get("CLICKUP_KEY") CLICKUP_SPACE = 757069 CLICKUP_TEAM = 754996 +GITHUB_URL_BOT = "https://github.com/discord-python/bot" +BOT_AVATAR_URL = "https://raw.githubusercontent.com/discord-python/branding/master/logos/logo_circle.png" + DEPLOY_URL = os.environ.get("DEPLOY_URL") STATUS_URL = os.environ.get("STATUS_URL") |