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
4249class 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