diff options
| -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") | 
