diff options
| -rw-r--r-- | bot/cogs/codeblock/cog.py | 63 | ||||
| -rw-r--r-- | bot/cogs/codeblock/instructions.py | 113 | ||||
| -rw-r--r-- | bot/cogs/codeblock/parsing.py | 2 |
3 files changed, 113 insertions, 65 deletions
diff --git a/bot/cogs/codeblock/cog.py b/bot/cogs/codeblock/cog.py index dad0cc9cc..efc22c8a5 100644 --- a/bot/cogs/codeblock/cog.py +++ b/bot/cogs/codeblock/cog.py @@ -1,6 +1,5 @@ import logging import time -from typing import Optional import discord from discord import Embed, Message, RawMessageUpdateEvent @@ -33,68 +32,6 @@ class CodeBlockCog(Cog, name="Code Block"): # Stores improperly formatted Python codeblock message ids and the corresponding bot message self.codeblock_message_ids = {} - def format_bad_ticks_message(self, message: discord.Message) -> Optional[str]: - """Return the guide message to output for bad code block ticks in `message`.""" - ticks = message.content[:3] - content = self.codeblock_stripping(f"```{message.content[3:-3]}```", True) - if content is None: - return - - content, repl_code = content - - if len(content) == 2: - content = content[1] - else: - content = content[0] - - content = parsing.truncate(content) - content_escaped_markdown = parsing.RE_MARKDOWN.sub(r'\\\1', content) - - return ( - "It looks like you are trying to paste code into this channel.\n\n" - "You seem to be using the wrong symbols to indicate where the codeblock should start. " - f"The correct symbols would be \\`\\`\\`, not `{ticks}`.\n\n" - "**Here is an example of how it should look:**\n" - f"\\`\\`\\`python\n{content_escaped_markdown}\n\\`\\`\\`\n\n" - "**This will result in the following:**\n" - f"```python\n{content}\n```" - ) - - def format_guide_message(self, message: discord.Message) -> Optional[str]: - """Return the guide message to output for a poorly formatted code block in `message`.""" - content = self.codeblock_stripping(message.content, False) - if content is None: - return - - content, repl_code = content - - if not repl_code and not parsing.is_python_code(content[0]): - return - - if content and repl_code: - content = content[1] - else: - content = content[0] - - content = parsing.truncate(content) - - log.debug( - f"{message.author} posted something that needed to be put inside python code " - f"blocks. Sending the user some instructions." - ) - - content_escaped_markdown = parsing.RE_MARKDOWN.sub(r'\\\1', content) - return ( - "It looks like you're trying to paste code into this channel.\n\n" - "Discord has support for Markdown, which allows you to post code with full " - "syntax highlighting. Please use these whenever you paste code, as this " - "helps improve the legibility and makes it easier for us to help you.\n\n" - f"**To do this, use the following method:**\n" - f"\\`\\`\\`python\n{content_escaped_markdown}\n\\`\\`\\`\n\n" - "**This will result in the following:**\n" - f"```python\n{content}\n```" - ) - @staticmethod def is_help_channel(channel: discord.TextChannel) -> bool: """Return True if `channel` is in one of the help categories.""" diff --git a/bot/cogs/codeblock/instructions.py b/bot/cogs/codeblock/instructions.py new file mode 100644 index 000000000..0bcd2eda8 --- /dev/null +++ b/bot/cogs/codeblock/instructions.py @@ -0,0 +1,113 @@ +import logging +from typing import Optional + +from . import parsing + +log = logging.getLogger(__name__) + +PY_LANG_CODES = ("python", "py") +EXAMPLE_PY = f"python\nprint('Hello, world!')" # Make sure to escape any Markdown symbols here. +EXAMPLE_CODE_BLOCKS = ( + "\\`\\`\\`{content}\n\\`\\`\\`\n\n" + "**This will result in the following:**\n" + "```{content}```" +) + + +def get_bad_ticks_message(code_block: parsing.CodeBlock) -> Optional[str]: + """Return instructions on using the correct ticks for `code_block`.""" + valid_ticks = f"\\{parsing.BACKTICK}" * 3 + + # The space at the end is important here because something may be appended! + instructions = ( + "It looks like you are trying to paste code into this channel.\n\n" + "You seem to be using the wrong symbols to indicate where the code block should start. " + f"The correct symbols would be {valid_ticks}, not `{code_block.tick * 3}`. " + ) + + # Check if the code has an issue with the language specifier. + addition_msg = get_bad_lang_message(code_block.content) + if not addition_msg: + addition_msg = get_no_lang_message(code_block.content) + + # Combine the back ticks message with the language specifier message. The latter will + # already have an example code block. + if addition_msg: + # The first line has a double line break which is not desirable when appending the msg. + addition_msg = addition_msg.replace("\n\n", "\n", 1) + + # Make the first character of the addition lower case. + instructions += "Furthermore, " + addition_msg[0].lower() + addition_msg[1:] + else: + # Determine the example code to put in the code block based on the language specifier. + if code_block.language.lower() in PY_LANG_CODES: + content = EXAMPLE_PY + elif code_block.language: + # It's not feasible to determine what would be a valid example for other languages. + content = f"{code_block.language}\n..." + else: + content = "Hello, world!" + + example_blocks = EXAMPLE_CODE_BLOCKS.format(content) + instructions += f"\n\n**Here is an example of how it should look:**\n{example_blocks}" + + return instructions + + +def get_no_ticks_message(content: str) -> Optional[str]: + """If `content` is Python/REPL code, return instructions on using code blocks.""" + if parsing.is_repl_code(content) or parsing.is_python_code(content): + example_blocks = EXAMPLE_CODE_BLOCKS.format(EXAMPLE_PY) + return ( + "It looks like you're trying to paste code into this channel.\n\n" + "Discord has support for Markdown, which allows you to post code with full " + "syntax highlighting. Please use these whenever you paste code, as this " + "helps improve the legibility and makes it easier for us to help you.\n\n" + f"**To do this, use the following method:**\n{example_blocks}" + ) + + +def get_bad_lang_message(content: str) -> Optional[str]: + """ + Return instructions on fixing the Python language specifier for a code block. + + If `content` doesn't start with "python" or "py" as the language specifier, return None. + """ + stripped = content.lstrip().lower() + lang = next((lang for lang in PY_LANG_CODES if stripped.startswith(lang)), None) + + if lang: + # Note that get_bad_ticks_message expects the first line to have an extra newline. + lines = ["It looks like you incorrectly specified a language for your code block.\n"] + + if content.startswith(" "): + lines.append(f"Make sure there are no spaces between the back ticks and `{lang}`.") + + if stripped[len(lang)] != "\n": + lines.append( + f"Make sure you put your code on a new line following `{lang}`. " + f"There must not be any spaces after `{lang}`." + ) + + example_blocks = EXAMPLE_CODE_BLOCKS.format(EXAMPLE_PY) + lines.append(f"\n**Here is an example of how it should look:**\n{example_blocks}") + + return "\n".join(lines) + + +def get_no_lang_message(content: str) -> Optional[str]: + """ + Return instructions on specifying a language for a code block. + + If `content` is not valid Python or Python REPL code, return None. + """ + if parsing.is_repl_code(content) or parsing.is_python_code(content): + example_blocks = EXAMPLE_CODE_BLOCKS.format(EXAMPLE_PY) + + # Note that get_bad_ticks_message expects the first line to have an extra newline. + return ( + "It looks like you pasted Python code without syntax highlighting.\n\n" + "Please use syntax highlighting to improve the legibility of your code and make" + "it easier for us to help you.\n\n" + f"**To do this, use the following method:**\n{example_blocks}" + ) diff --git a/bot/cogs/codeblock/parsing.py b/bot/cogs/codeblock/parsing.py index 7a096758b..d541441e0 100644 --- a/bot/cogs/codeblock/parsing.py +++ b/bot/cogs/codeblock/parsing.py @@ -7,8 +7,6 @@ import discord log = logging.getLogger(__name__) -RE_MARKDOWN = re.compile(r'([*_~`|>])') -RE_CODE_BLOCK_LANGUAGE = re.compile(r"```(?:[^\W_]+)\n(.*?)```", re.DOTALL) BACKTICK = "`" TICKS = { BACKTICK, |