Skip to content

Commit 0112e55

Browse files
Merge pull request #3310 from python-discord/vivek/pastebin-codeblock
Display selected lines from our pastebin as codeblock
2 parents f971081 + 9084991 commit 0112e55

1 file changed

Lines changed: 64 additions & 14 deletions

File tree

bot/exts/info/code_snippets.py

Lines changed: 64 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@
3838
r"/(?P<file_path>[^#>]+)(\?[^#>]+)?(#lines-(?P<start_line>\d+)(:(?P<end_line>\d+))?)"
3939
)
4040

41+
PYDIS_PASTEBIN_RE = re.compile(
42+
r"https://paste\.(?:pythondiscord\.com|pydis\.wtf)/(?P<paste_id>[a-zA-Z0-9]+)"
43+
r"#(?P<selections>(?:\d+L\d+-L\d+)(?:,\d+L\d+-L\d+)*)"
44+
)
45+
46+
PASTEBIN_LINE_SELECTION_RE = re.compile(r"(\d+)L(\d+)-L(\d+)")
47+
4148

4249
class CodeSnippets(Cog):
4350
"""
@@ -54,7 +61,8 @@ def __init__(self, bot: Bot):
5461
(GITHUB_RE, self._fetch_github_snippet),
5562
(GITHUB_GIST_RE, self._fetch_github_gist_snippet),
5663
(GITLAB_RE, self._fetch_gitlab_snippet),
57-
(BITBUCKET_RE, self._fetch_bitbucket_snippet)
64+
(BITBUCKET_RE, self._fetch_bitbucket_snippet),
65+
(PYDIS_PASTEBIN_RE, self._fetch_pastebin_snippets),
5866
]
5967

6068
async def _fetch_response(self, url: str, response_format: str, **kwargs) -> Any:
@@ -170,7 +178,40 @@ async def _fetch_bitbucket_snippet(
170178
)
171179
return self._snippet_to_codeblock(file_contents, file_path, start_line, end_line)
172180

173-
def _snippet_to_codeblock(self, file_contents: str, file_path: str, start_line: str, end_line: str) -> str:
181+
async def _fetch_pastebin_snippets(self, paste_id: str, selections: str) -> list[str]:
182+
"""Fetches snippets from paste.pythondiscord.com."""
183+
paste_data = await self._fetch_response(
184+
f"https://paste.pythondiscord.com/api/v1/paste/{paste_id}",
185+
"json"
186+
)
187+
188+
snippets = []
189+
for match in PASTEBIN_LINE_SELECTION_RE.finditer(selections):
190+
file_num, start, end = match.groups()
191+
file_num = int(file_num) - 1
192+
193+
file = paste_data["files"][file_num]
194+
file_name = file.get("name") or f"file {file_num + 1}"
195+
snippet = self._snippet_to_codeblock(
196+
file["content"],
197+
file_name,
198+
start,
199+
end,
200+
language=file["lexer"],
201+
)
202+
203+
snippets.append(snippet)
204+
205+
return snippets
206+
207+
def _snippet_to_codeblock(
208+
self,
209+
file_contents: str,
210+
file_path: str,
211+
start_line: str,
212+
end_line: str|None,
213+
language: str|None = None
214+
) -> str:
174215
"""
175216
Given the entire file contents and target lines, creates a code block.
176217
@@ -203,15 +244,16 @@ def _snippet_to_codeblock(self, file_contents: str, file_path: str, start_line:
203244
required = "\n".join(split_file_contents[start_line - 1:end_line])
204245
required = textwrap.dedent(required).rstrip().replace("`", "`\u200b")
205246

206-
# Extracts the code language and checks whether it's a "valid" language
207-
language = file_path.split("/")[-1].split(".")[-1]
208-
trimmed_language = language.replace("-", "").replace("+", "").replace("_", "")
209-
is_valid_language = trimmed_language.isalnum()
210-
if not is_valid_language:
211-
language = ""
247+
if language is None:
248+
# Extracts the code language and checks whether it's a "valid" language
249+
language = file_path.split("/")[-1].split(".")[-1]
250+
trimmed_language = language.replace("-", "").replace("+", "").replace("_", "")
251+
is_valid_language = trimmed_language.isalnum()
252+
if not is_valid_language:
253+
language = ""
212254

213-
if language == "pyi":
214-
language = "py"
255+
if language == "pyi":
256+
language = "py"
215257

216258
# Adds a label showing the file path to the snippet
217259
if start_line == end_line:
@@ -231,8 +273,7 @@ async def _parse_snippets(self, content: str) -> str:
231273
for pattern, handler in self.pattern_handlers:
232274
for match in pattern.finditer(content):
233275
try:
234-
snippet = await handler(**match.groupdict())
235-
all_snippets.append((match.start(), snippet))
276+
result = await handler(**match.groupdict())
236277
except ClientResponseError as error:
237278
error_message = error.message
238279
log.log(
@@ -241,8 +282,17 @@ async def _parse_snippets(self, content: str) -> str:
241282
f"{error_message} for GET {error.request_info.real_url.human_repr()}"
242283
)
243284

244-
# Sorts the list of snippets by their match index and joins them into a single message
245-
return "\n".join(x[1] for x in sorted(all_snippets))
285+
if isinstance(result, list):
286+
# The handler returned multiple snippets (currently only possible with our pastebin)
287+
all_snippets.extend((match.start(), snippet) for snippet in result)
288+
else:
289+
all_snippets.append((match.start(), result))
290+
291+
# Sort the list of snippets by ONLY their match index
292+
all_snippets.sort(key=lambda item: item[0])
293+
294+
# Join them into a single message
295+
return "\n".join(x[1] for x in all_snippets)
246296

247297
@Cog.listener()
248298
async def on_message(self, message: discord.Message) -> None:

0 commit comments

Comments
 (0)