Ejemplo n.º 1
0
    def should_parse(self, message: discord.Message) -> bool:
        """
        Return True if `message` should be parsed.

        A qualifying message:

        1. Is not authored by a bot
        2. Is in a valid channel
        3. Has more than 3 lines
        4. Has no bot or webhook token
        """
        return (not message.author.bot
                and self.is_valid_channel(message.channel) and has_lines(
                    message.content, constants.CodeBlock.minimum_lines)
                and not TokenRemover.find_token_in_message(message)
                and not WEBHOOK_URL_RE.search(message.content))
Ejemplo n.º 2
0
    async def relay_message(self, msg: Message) -> None:
        """Relays the message to the relevant watch channel."""
        limit = BigBrotherConfig.header_message_limit

        if (
            msg.author.id != self.message_history.last_author
            or msg.channel.id != self.message_history.last_channel
            or self.message_history.message_count >= limit
        ):
            self.message_history = MessageHistory(last_author=msg.author.id, last_channel=msg.channel.id)

            await self.send_header(msg)

        if TokenRemover.find_token_in_message(msg) or WEBHOOK_URL_RE.search(msg.content):
            cleaned_content = "Content is censored because it contains a bot or webhook token."
        elif cleaned_content := msg.clean_content:
            # Put all non-media URLs in a code block to prevent embeds
            media_urls = {embed.url for embed in msg.embeds if embed.type in ("image", "video")}
            for url in URL_RE.findall(cleaned_content):
                if url not in media_urls:
                    cleaned_content = cleaned_content.replace(url, f"`{url}`")
Ejemplo n.º 3
0
    async def on_message(self, msg: Message) -> None:
        """
        Detect poorly formatted Python code in new messages.

        If poorly formatted code is detected, send the user a helpful message explaining how to do
        properly formatted Python syntax highlighting codeblocks.
        """
        is_help_channel = (getattr(msg.channel, "category", None)
                           and msg.channel.category.id
                           in (Categories.help_available,
                               Categories.help_in_use))
        parse_codeblock = ((is_help_channel
                            or msg.channel.id in self.channel_cooldowns
                            or msg.channel.id in self.channel_whitelist)
                           and not msg.author.bot
                           and len(msg.content.splitlines()) > 3
                           and not TokenRemover.find_token_in_message(msg)
                           and not WEBHOOK_URL_RE.search(msg.content))

        if parse_codeblock:  # no token in the msg
            on_cooldown = (time.time() -
                           self.channel_cooldowns.get(msg.channel.id, 0)) < 300
            if not on_cooldown or DEBUG_MODE:
                try:
                    if self.has_bad_ticks(msg):
                        ticks = msg.content[:3]
                        content = self.codeblock_stripping(
                            f"```{msg.content[3:-3]}```", True)
                        if content is None:
                            return

                        content, repl_code = content

                        if len(content) == 2:
                            content = content[1]
                        else:
                            content = content[0]

                        space_left = 204
                        if len(content) >= space_left:
                            current_length = 0
                            lines_walked = 0
                            for line in content.splitlines(keepends=True):
                                if current_length + len(
                                        line
                                ) > space_left or lines_walked == 10:
                                    break
                                current_length += len(line)
                                lines_walked += 1
                            content = content[:current_length] + "#..."
                        content_escaped_markdown = RE_MARKDOWN.sub(
                            r'\\\1', content)
                        howto = (
                            "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```")

                    else:
                        howto = ""
                        content = self.codeblock_stripping(msg.content, False)
                        if content is None:
                            return

                        content, repl_code = content
                        # Attempts to parse the message into an AST node.
                        # Invalid Python code will raise a SyntaxError.
                        tree = ast.parse(content[0])

                        # Multiple lines of single words could be interpreted as expressions.
                        # This check is to avoid all nodes being parsed as expressions.
                        # (e.g. words over multiple lines)
                        if not all(
                                isinstance(node, ast.Expr)
                                for node in tree.body) or repl_code:
                            # Shorten the code to 10 lines and/or 204 characters.
                            space_left = 204
                            if content and repl_code:
                                content = content[1]
                            else:
                                content = content[0]

                            if len(content) >= space_left:
                                current_length = 0
                                lines_walked = 0
                                for line in content.splitlines(keepends=True):
                                    if current_length + len(
                                            line
                                    ) > space_left or lines_walked == 10:
                                        break
                                    current_length += len(line)
                                    lines_walked += 1
                                content = content[:current_length] + "#..."

                            content_escaped_markdown = RE_MARKDOWN.sub(
                                r'\\\1', content)
                            howto += (
                                "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```")

                            log.debug(
                                f"{msg.author} posted something that needed to be put inside python code "
                                "blocks. Sending the user some instructions.")
                        else:
                            log.trace(
                                "The code consists only of expressions, not sending instructions"
                            )

                    if howto != "":
                        # Increase amount of codeblock correction in stats
                        self.bot.stats.incr("codeblock_corrections")
                        howto_embed = Embed(description=howto)
                        bot_message = await msg.channel.send(
                            f"Hey {msg.author.mention}!", embed=howto_embed)
                        self.codeblock_message_ids[msg.id] = bot_message.id

                        self.bot.loop.create_task(
                            wait_for_deletion(bot_message, (msg.author.id, ),
                                              self.bot))
                    else:
                        return

                    if msg.channel.id not in self.channel_whitelist:
                        self.channel_cooldowns[msg.channel.id] = time.time()

                except SyntaxError:
                    log.trace(
                        f"{msg.author} posted in a help channel, and when we tried to parse it as Python code, "
                        "ast.parse raised a SyntaxError. This probably just means it wasn't Python code. "
                        f"The message that was posted was:\n\n{msg.content}\n\n"
                    )