aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bot/__main__.py1
-rw-r--r--bot/cogs/source.py99
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))