diff options
author | 2020-09-29 13:41:59 +0530 | |
---|---|---|
committer | 2020-09-29 13:41:59 +0530 | |
commit | bd3dd9fa32a4f871b7033f0d322e5b675d51b8b1 (patch) | |
tree | d54efc957ae0512933a7c1ec8402ab9a909edb2f /bot/exts/evergreen/source.py | |
parent | added github url of seasonal bot (diff) |
added source command
Diffstat (limited to 'bot/exts/evergreen/source.py')
-rw-r--r-- | bot/exts/evergreen/source.py | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/bot/exts/evergreen/source.py b/bot/exts/evergreen/source.py new file mode 100644 index 00000000..164430e2 --- /dev/null +++ b/bot/exts/evergreen/source.py @@ -0,0 +1,126 @@ +import inspect +from pathlib import Path +from typing import Optional, Tuple, Union + +from discord import Embed +from discord.ext import commands + +from bot.constants import Source + +SourceType = Union[commands.HelpCommand, commands.Command, commands.Cog, str, commands.ExtensionNotLoaded] + + +class SourceConverter(commands.Converter): + """Convert an argument into a help command, tag, command, or cog.""" + + async def convert(self, ctx: commands.Context, argument: str) -> SourceType: + """Convert argument into source object.""" + cog = ctx.bot.get_cog(argument) + if cog: + return cog + + cmd = ctx.bot.get_command(argument) + if cmd: + return cmd + + raise commands.BadArgument( + f"Unable to convert `{argument}` to valid command or Cog." + ) + + +class BotSource(commands.Cog): + """Displays information about the bot's source code.""" + + def __init__(self, bot: commands.Bot): + self.bot = bot + + @commands.command(name="source", aliases=("src",)) + async def source_command(self, ctx: commands.Context, *, source_item: SourceConverter = None) -> None: + """Display information and a GitHub link to the source code of a command, tag, or cog.""" + if not source_item: + embed = Embed(title="Bot's GitHub Repository") + embed.add_field(name="Repository", value=f"[Go to GitHub]({Source.github})") + embed.set_thumbnail(url="https://avatars1.githubusercontent.com/u/9919") + await ctx.send(embed=embed) + return + + embed = await self.build_embed(source_item) + await ctx.send(embed=embed) + + def get_source_link(self, source_item: SourceType) -> Tuple[str, str, Optional[int]]: + """ + Build GitHub link of source item, return this link, file location and first line number. + + Raise BadArgument if `source_item` is a dynamically-created object (e.g. via internal eval). + """ + if isinstance(source_item, commands.Command): + if source_item.cog_name == "Alias": + cmd_name = source_item.callback.__name__.replace("_alias", "") + cmd = self.bot.get_command(cmd_name.replace("_", " ")) + src = cmd.callback.__code__ + filename = src.co_filename + else: + src = source_item.callback.__code__ + filename = src.co_filename + else: + src = type(source_item) + try: + filename = inspect.getsourcefile(src) + except TypeError: + raise commands.BadArgument("Cannot get source for a dynamically-created object.") + + if not isinstance(source_item, str): + try: + lines, first_line_no = inspect.getsourcelines(src) + except OSError: + raise commands.BadArgument("Cannot get source for a dynamically-created object.") + + lines_extension = f"#L{first_line_no}-L{first_line_no+len(lines)-1}" + else: + first_line_no = None + lines_extension = "" + + # Handle tag file location differently than others to avoid errors in some cases + if not first_line_no: + file_location = Path(filename).relative_to("/bot/") + else: + file_location = Path(filename).relative_to(Path.cwd()).as_posix() + + url = f"{Source.github}/blob/master/{file_location}{lines_extension}" + + return url, file_location, first_line_no or None + + async def build_embed(self, source_object: SourceType) -> Optional[Embed]: + """Build embed based on source object.""" + url, location, first_line = self.get_source_link(source_object) + + if isinstance(source_object, commands.HelpCommand): + title = "Help Command" + description = source_object.__doc__.splitlines()[1] + elif isinstance(source_object, commands.Command): + if source_object.cog_name == "Alias": + cmd_name = source_object.callback.__name__.replace("_alias", "") + cmd = self.bot.get_command(cmd_name.replace("_", " ")) + description = cmd.short_doc + else: + description = source_object.short_doc + + title = f"Command: {source_object.qualified_name}" + elif isinstance(source_object, str): + title = f"Tag: {source_object}" + description = "" + else: + title = f"Cog: {source_object.qualified_name}" + description = source_object.description.splitlines()[0] + + embed = Embed(title=title, description=description) + embed.add_field(name="Source Code", value=f"[Go to GitHub]({url})") + line_text = f":{first_line}" if first_line else "" + embed.set_footer(text=f"{location}{line_text}") + + return embed + + +def setup(bot: commands.Bot) -> None: + """Load the BotSource cog.""" + bot.add_cog(BotSource(bot)) |