async def jsk_python_inspect(self, ctx: commands.Context, *, argument: codeblock_converter): """ Evaluation of Python code with inspect information. """ arg_dict = get_var_dict_from_ctx(ctx, Flags.SCOPE_PREFIX) arg_dict["_"] = self.last_result scope = self.scope try: async with ReplResponseReactor(ctx.message): with self.submit(ctx): executor = AsyncCodeExecutor(argument.content, scope, arg_dict=arg_dict) async for send, result in AsyncSender(executor): self.last_result = result header = repr(result).replace("``", "`\u200b`").replace( self.bot.http.token, "[token omitted]") if len(header) > 485: header = header[0:482] + "..." lines = [f"=== {header} ===", ""] for name, res in all_inspections(result): lines.append(f"{name:16.16} :: {res}") docstring = (inspect.getdoc(result) or '').strip() if docstring: lines.append(f"\n=== Help ===\n\n{docstring}") text = "\n".join(lines) if use_file_check(ctx, len( text)): # File "full content" preview limit send(await ctx.send(file=discord.File( filename="inspection.prolog", fp=io.BytesIO(text.encode('utf-8'))))) else: paginator = WrappedPaginator(prefix="```prolog", max_size=1985) paginator.add_line(text) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) send(await interface.send_to(ctx)) finally: scope.clear_intersection(arg_dict)
async def jsk_cat(self, ctx: commands.Context, argument: str): # pylint: disable=too-many-locals """ Read out a file, using syntax highlighting if detected. Lines and linespans are supported by adding '#L12' or '#L12-14' etc to the end of the filename. """ match = self.__cat_line_regex.search(argument) if not match: # should never happen return await ctx.send("Couldn't parse this input.") path = match.group(1) line_span = None if match.group(2): start = int(match.group(2)) line_span = (start, int(match.group(3) or start)) if not os.path.exists(path) or os.path.isdir(path): return await ctx.send(f"`{path}`: No file by that name.") size = os.path.getsize(path) if size <= 0: return await ctx.send(f"`{path}`: Cowardly refusing to read a file with no size stat" f" (it may be empty, endless or inaccessible).") if size > 128 * (1024 ** 2): return await ctx.send(f"`{path}`: Cowardly refusing to read a file >128MB.") try: with open(path, "rb") as file: if use_file_check(ctx, size): if line_span: content, *_ = guess_file_traits(file.read()) lines = content.split('\n')[line_span[0] - 1:line_span[1]] await ctx.send(file=discord.File( filename=pathlib.Path(file.name).name, fp=io.BytesIO('\n'.join(lines).encode('utf-8')) )) else: await ctx.send(file=discord.File( filename=pathlib.Path(file.name).name, fp=file )) else: paginator = WrappedFilePaginator(file, line_span=line_span, max_size=1985) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) await interface.send_to(ctx) except UnicodeDecodeError: return await ctx.send(f"`{path}`: Couldn't determine the encoding of this file.") except ValueError as exc: return await ctx.send(f"`{path}`: Couldn't read this file, {exc}")
async def jsk_curl(self, ctx: commands.Context, url: str): """ Download and display a text file from the internet. This command is similar to jsk cat, but accepts a URL. """ # remove embed maskers if present url = url.lstrip("<").rstrip(">") async with ReplResponseReactor(ctx.message): async with aiohttp.ClientSession() as session: async with session.get(url) as response: data = await response.read() hints = (response.content_type, url) code = response.status if not data: return await ctx.send( f"HTTP response was empty (status code {code}).") if use_file_check(ctx, len(data)): # File "full content" preview limit # Shallow language detection language = None for hint in hints: language = get_language(hint) if language: break await ctx.send( file=disnake.File(filename=f"response.{language or 'txt'}", fp=io.BytesIO(data))) else: try: paginator = WrappedFilePaginator(io.BytesIO(data), language_hints=hints, max_size=1985) except UnicodeDecodeError: return await ctx.send( f"Couldn't determine the encoding of the response. (status code {code})" ) except ValueError as exc: return await ctx.send( f"Couldn't read response (status code {code}), {exc}") interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) await interface.send_to(ctx)
async def jsk_python_result_handling(self, ctx: commands.Context, result): # pylint: disable=too-many-return-statements """ Determines what is done with a result when it comes out of jsk py. This allows you to override how this is done without having to rewrite the command itself. What you return is what gets stored in the temporary _ variable. """ if isinstance(result, discord.Message): return await ctx.send(f"<Message <{result.jump_url}>>") if isinstance(result, discord.File): return await ctx.send(file=result) if isinstance(result, discord.Embed): return await ctx.send(embed=result) if isinstance(result, PaginatorInterface): return await result.send_to(ctx) if not isinstance(result, str): # repr all non-strings result = repr(result) # Eventually the below handling should probably be put somewhere else if len(result) <= 2000: if result.strip() == '': result = "\u200b" return await ctx.send( result.replace(self.bot.http.token, "[token omitted]"), allowed_mentions=discord.AllowedMentions.none()) if use_file_check(ctx, len(result)): # File "full content" preview limit # Discord's desktop and web client now supports an interactive file content # display for files encoded in UTF-8. # Since this avoids escape issues and is more intuitive than pagination for # long results, it will now be prioritized over PaginatorInterface if the # resultant content is below the filesize threshold return await ctx.send(file=discord.File( filename="output.py", fp=io.BytesIO(result.encode('utf-8')))) # inconsistency here, results get wrapped in codeblocks when they are too large # but don't if they're not. probably not that bad, but noting for later review paginator = WrappedPaginator(prefix='```py', suffix='```', max_size=1985) paginator.add_line(result) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) return await interface.send_to(ctx)
async def jsk_source(self, ctx: commands.Context, *, command_name: str): """ Displays the source code for a command. """ command = self.bot.get_command(command_name) or self.get_slash_command( command_name) if not command: return await ctx.send(f"Couldn't find command `{command_name}`.") try: source_lines, _ = inspect.getsourcelines(command.callback) except (TypeError, OSError): return await ctx.send( f"Was unable to retrieve the source for `{command}` for some reason." ) filename = "source.py" try: filename = pathlib.Path(inspect.getfile(command.callback)).name except (TypeError, OSError): pass # getsourcelines for some reason returns WITH line endings source_text = ''.join(source_lines) if use_file_check( ctx, len(source_text)): # File "full content" preview limit await ctx.send(file=disnake.File( filename=filename, fp=io.BytesIO(source_text.encode('utf-8')))) else: paginator = WrappedPaginator(prefix='```py', suffix='```', max_size=1985) paginator.add_line( source_text.replace('```', '``\N{zero width space}`')) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) await interface.send_to(ctx)
async def jsk_disassemble(self, ctx: commands.Context, *, argument: codeblock_converter): """ Disassemble Python code into bytecode. """ arg_dict = get_var_dict_from_ctx(ctx, Flags.SCOPE_PREFIX) async with ReplResponseReactor(ctx.message): text = "\n".join(disassemble(argument.content, arg_dict=arg_dict)) if use_file_check(ctx, len(text)): # File "full content" preview limit await ctx.send(file=discord.File( filename="dis.py", fp=io.BytesIO(text.encode('utf-8')))) else: paginator = WrappedPaginator(prefix='```py', max_size=1985) paginator.add_line(text) interface = PaginatorInterface(ctx.bot, paginator, owner=ctx.author) await interface.send_to(ctx)