diff options
| -rw-r--r-- | bot/__main__.py | 1 | ||||
| -rw-r--r-- | bot/cogs/source.py | 99 |
2 files changed, 100 insertions, 0 deletions
diff --git a/bot/__main__.py b/bot/__main__.py index aa1d1aee8..d82adc802 100644 --- a/bot/__main__.py +++ b/bot/__main__.py @@ -57,6 +57,7 @@ bot.load_extension("bot.cogs.reddit") bot.load_extension("bot.cogs.reminders") bot.load_extension("bot.cogs.site") bot.load_extension("bot.cogs.snekbox") +bot.load_extension("bot.cogs.source") bot.load_extension("bot.cogs.stats") bot.load_extension("bot.cogs.sync") bot.load_extension("bot.cogs.tags") diff --git a/bot/cogs/source.py b/bot/cogs/source.py new file mode 100644 index 000000000..6e71ae5b2 --- /dev/null +++ b/bot/cogs/source.py @@ -0,0 +1,99 @@ +import inspect +import os +from typing import Union + +from discord import Embed +from discord.ext.commands import BadArgument, Cog, Command, Context, Converter, HelpCommand, command + +from bot.bot import Bot +from bot.constants import URLs + + +class SourceConverter(Converter): + """Convert an argument into a help command, command, or cog.""" + + async def convert(self, ctx: Context, argument: str) -> Union[HelpCommand, Command, Cog]: + """ + Convert argument into source object. + + Order how arguments is checked: + 1. When argument is `help`, return bot help command + 2. When argument is valid command, return this command + 3. When argument is valid Cog, return this Cog + 4. Otherwise raise `BadArgument` error + """ + if argument.lower() == "help": + return ctx.bot.help_command + + cmd = ctx.bot.get_command(argument) + if cmd: + return cmd + + cog = ctx.bot.get_cog(argument) + if cog: + return cog + + raise BadArgument(f"Unable to convert `{argument}` to valid command or Cog.") + + +class BotSource(Cog): + """Displays information about the bot's source code.""" + + def __init__(self, bot: Bot): + self.bot = bot + + @command(name="source", aliases=("src",)) + async def source_command(self, ctx: Context, *, source_item: SourceConverter = None) -> None: + """Display information and a GitHub link to the source code of a command or cog.""" + if not source_item: + embed = Embed(title="Bot GitHub Repository") + embed.add_field(name="Repository", value=f"[Go to GitHub]({URLs.github_bot_repo})") + await ctx.send(embed=embed) + return + + url = self.get_source_link(source_item) + await ctx.send(embed=await self.build_embed(url, source_item, ctx)) + + @staticmethod + def get_source_link(source_item: Union[HelpCommand, Command, Cog]) -> str: + """Build GitHub link of source item.""" + if isinstance(source_item, HelpCommand): + src = type(source_item) + filename = inspect.getsourcefile(src) + elif isinstance(source_item, Command): + src = source_item.callback.__code__ + filename = src.co_filename + else: + src = type(source_item) + filename = inspect.getsourcefile(src) + + lines, first_line_no = inspect.getsourcelines(src) + file_location = os.path.relpath(filename) + + return f"{URLs.github_bot_repo}/blob/master/{file_location}#L{first_line_no}-L{first_line_no+len(lines)-1}" + + @staticmethod + async def build_embed(link: str, source_object: Union[HelpCommand, Command, Cog], ctx: Context) -> Embed: + """Build embed based on source object.""" + if isinstance(source_object, HelpCommand): + title = "Help" + description = source_object.__doc__ + elif isinstance(source_object, Command): + title = source_object.qualified_name + description = source_object.help + else: + title = source_object.qualified_name + description = source_object.description + + embed = Embed(title=title, description=description) + embed.add_field(name="Source Code", value=f"[Go to GitHub]({link})") + + if isinstance(source_object, Command): + embed.add_field(name="Can be used by you here?", value=await source_object.can_run(ctx)) + + return embed + + +def setup(bot: Bot) -> None: + """Load the BotSource cog.""" + bot.add_cog(BotSource(bot)) |