1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
import inspect
from pathlib import Path
from typing import Optional, Tuple
from discord import Embed
from discord.ext import commands
from bot.bot import Bot
from bot.constants import Source
from bot.utils.converters import SourceConverter, SourceType
class BotSource(commands.Cog):
"""Displays information about the bot's source code."""
@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="Sir Lancebot's GitHub Repository")
embed.add_field(name="Repository", value=f"[Go to GitHub]({Source.github})")
embed.set_thumbnail(url=Source.github_avatar_url)
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):
callback = inspect.unwrap(source_item.callback)
src = 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 = ""
file_location = Path(filename).relative_to(Path.cwd()).as_posix()
url = f"{Source.github}/blob/main/{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.Command):
description = source_object.short_doc
title = f"Command: {source_object.qualified_name}"
else:
title = f"Cog: {source_object.qualified_name}"
description = source_object.description.splitlines()[0]
embed = Embed(title=title, description=description)
embed.set_thumbnail(url=Source.github_avatar_url)
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: Bot) -> None:
"""Load the BotSource cog."""
bot.add_cog(BotSource())
|