Source code for discordSuperUtils.MessageFilter

from __future__ import annotations

import asyncio
import re
from abc import ABC, abstractmethod
from datetime import timedelta
from typing import (
    TYPE_CHECKING,
    Union,
    Any,
    List
)

from .Base import get_generator_response, EventManager
from .Punishments import get_relevant_punishment

if TYPE_CHECKING:
    from discord.ext import commands
    import discord
    from .Punishments import Punishment

__all__ = (
    "MessageFilter",
    "MessageResponseGenerator",
    "DefaultMessageResponseGenerator"
)


[docs]class MessageResponseGenerator(ABC): """ Represents a URL response generator that filters messages and checks if they contain URLs or anything inappropriate. """ __slots__ = ()
[docs] @abstractmethod def generate(self, message: discord.Message) -> Union[bool, Any]: """ This function is an abstract method. The generate function of the generator. :param message: The message to filter. :type message: discord.Message :return: A boolean representing if the message contains inappropriate content. :rtype: Union[bool, Any] """ pass
[docs]class DefaultMessageResponseGenerator(MessageResponseGenerator): URL_RE = re.compile(r"(https?://(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][" r"a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?://(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2," r"}|www\.[a-zA-Z0-9]+\.[^\s]{2,})") DISCORD_INVITE_RE = re.compile(r"(?:(?:http|https)://)?(?:www.)?(?:disco|discord|discordapp).(" r"?:com|gg|io|li|me|net|org)(?:/(?:invite))?/([a-z0-9-.]+)")
[docs] def generate(self, message: discord.Message) -> Union[bool, Any]: if message.author.guild_permissions.administrator: return False return self.URL_RE.match(message.content) or self.DISCORD_INVITE_RE.match(message.content)
[docs]class MessageFilter(EventManager): """ Represents a discordSuperUtils message filter that filters messages and finds inappropriate content. """ __slots__ = ("bot", "generator", "_member_cache", "punishments", "wipe_cache_delay") def __init__(self, bot: commands.Bot, generator: MessageResponseGenerator = None, delete_message: bool = True, wipe_cache_delay: timedelta = timedelta(minutes=5)): super().__init__() self.bot = bot self.generator = generator if generator is not None else DefaultMessageResponseGenerator self.delete_message = delete_message self.wipe_cache_delay = wipe_cache_delay self._member_cache = {} self.punishments = [] self.bot.loop.create_task(self.__wipe_cache()) self.bot.add_listener(self.__handle_messages, 'on_message') self.bot.add_listener(self.__handle_messages, 'on_message_edit') async def __wipe_cache(self): """ This function is responsible for wiping the member cache. :return: """ while not self.bot.is_closed(): await asyncio.sleep(self.wipe_cache_delay.total_seconds()) self._member_cache = {}
[docs] def add_punishments(self, punishments: List[Punishment]) -> None: self.punishments = punishments
async def __handle_messages(self, message, edited_message=None): """ This function is the main logic of the MessageFilter, Handled events: on_message, on_message_edit :param message: The on_message message passed by the event. :type message: discord.Message :param edited_message: The edited messages passed by the on_message_edit event, this function will use this incase it is not None. :type edited_message: discord.Message :return: """ message = edited_message or message if not message.guild or message.author.bot: return if not get_generator_response(self.generator, MessageResponseGenerator, message): return if self.delete_message: await message.delete() member_warnings = self._member_cache.setdefault(message.guild.id, {}).get(message.author.id, 0) + 1 self._member_cache[message.guild.id][message.author.id] = member_warnings await self.call_event("on_inappropriate_message", message, member_warnings) if punishment := get_relevant_punishment(self.punishments, member_warnings): await punishment.punishment_manager.punish(message, message.author, punishment)