Skip to content

Commit 2fefc5e

Browse files
committed
Add github codeblock formatting with embds & ANSI
1 parent a5a53ff commit 2fefc5e

File tree

1 file changed

+102
-0
lines changed

1 file changed

+102
-0
lines changed

modules/github.py

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import re
2626
from typing import TYPE_CHECKING
2727

28+
import aiohttp
2829
import discord
2930

3031
import core
@@ -35,6 +36,9 @@
3536
GITHUB_ISSUE_URL = "https://github.com/{}/issues/{}"
3637
LIB_ISSUE_REGEX = re.compile(r"(?P<lib>[a-z]+)?##(?P<number>[0-9]+)", flags=re.IGNORECASE)
3738

39+
GITHUB_BASE_URL = "https://github.com/"
40+
GITHUB_RAW_CONTENT_URL = "https://raw.githubusercontent.com/"
41+
3842
aliases = [
3943
(("wavelink", "wave", "wl"), "PythonistaGuild/Wavelink"),
4044
(("discordpy", "discord", "dpy"), "Rapptz/discord.py"),
@@ -46,6 +50,75 @@
4650
class GitHub(core.Cog):
4751
def __init__(self, bot: Bot) -> None:
4852
self.bot: Bot = bot
53+
self.code_highlight_emoji = "📃"
54+
55+
def _strip_content_path(self, url: str) -> str:
56+
file_path = url[len(GITHUB_BASE_URL):]
57+
return file_path
58+
59+
async def format_highlight_block(self, url: str, line_adjustment: int = 10):
60+
try:
61+
highlighted_line = int(url.split("#L")[1]) # seperate the #L{n} highlight
62+
except:
63+
return None
64+
65+
file_path = self._strip_content_path(url)
66+
raw_url = GITHUB_RAW_CONTENT_URL + file_path.replace("blob/", "") # Convert it to a raw user content URL
67+
68+
code = ""
69+
async with aiohttp.ClientSession() as session:
70+
async with session.get(raw_url) as r:
71+
if r.status == 404:
72+
return
73+
74+
code += await r.text()
75+
76+
code = code.splitlines()
77+
78+
code_block_dict = {"lines": {}}
79+
j = 0
80+
for i in code:
81+
# populate the dict
82+
code_block_dict["lines"][j] = i
83+
j += 1
84+
85+
code_block_dict["lines"][j] = "\n"
86+
87+
line_list = code_block_dict["lines"]
88+
89+
if highlighted_line - 1 not in line_list:
90+
return None
91+
92+
bound_adj = line_adjustment # adjustment for upper and lower bound display
93+
_minBoundary = (highlighted_line - 1 - bound_adj)
94+
_maxBoundary = (highlighted_line - 1 + bound_adj)
95+
96+
# loop through all the lines, and adjust the formatting
97+
msg = "```ansi\n"
98+
key = _minBoundary
99+
while key <= _maxBoundary:
100+
currLineNum = str(key + 1)
101+
# insert a space if there is no following char before the first character...
102+
if key + 1 == highlighted_line:
103+
highlighted_msg_format = "\u001b[0;37m\u001b[4;31m{} {}\u001b[0;0m\n".format(
104+
currLineNum, line_list[key]
105+
)
106+
107+
msg += highlighted_msg_format
108+
else:
109+
display_str = "{} {}\n" if line_list.get(key) is not None else "" # if we hit the end of the file, just write an empty string
110+
msg += display_str.format(currLineNum, line_list.get(key))
111+
key += 1
112+
113+
msg += "\n```"
114+
115+
github_dict = {
116+
"path": file_path,
117+
"min": _minBoundary if _minBoundary > 0 else highlighted_line, # Do not display negative numbers if >0
118+
"max": _maxBoundary,
119+
"msg": msg
120+
}
121+
return github_dict
49122

50123
@core.Cog.listener()
51124
async def on_message(self, message: discord.Message) -> None:
@@ -60,6 +133,35 @@ async def on_message(self, message: discord.Message) -> None:
60133

61134
await message.channel.send(GITHUB_ISSUE_URL.format(lib, issue))
62135

136+
codeSegment = await self.format_highlight_block(message.content)
137+
138+
if codeSegment is None:
139+
return
140+
141+
await message.add_reaction(self.code_highlight_emoji)
142+
143+
path = codeSegment['path']
144+
_min = codeSegment['min']
145+
_max = codeSegment['max']
146+
code_fmt = codeSegment['msg']
147+
148+
def check(reaction, user):
149+
return reaction.emoji == self.code_highlight_emoji and user != self.bot.user \
150+
and message.id == reaction.message.id
151+
152+
await self.bot.wait_for("reaction_add", check=check)
153+
154+
code_display_msg = await message.channel.send(
155+
content="Showing lines `{}` - `{}` in: `{}`...\n{}".format(_min, _max, path, code_fmt),
156+
suppress_embeds=True
157+
)
158+
159+
await self.bot.wait_for("reaction_remove", check=check)
160+
await code_display_msg.delete()
161+
162+
# clean up reactions
163+
await message.clear_reactions()
164+
63165

64166
async def setup(bot: Bot) -> None:
65167
await bot.add_cog(GitHub(bot))

0 commit comments

Comments
 (0)