Ejemplo n.º 1
0
    def remove_ignored_item(self, item: int, ignore_type: str) -> None:
        """
        Remove an item from the relevant ignore list

        Parameters
        ----------
        item : int
            The id of the thing to unignore
        ignore_type : str
            A string representation of the ignored
            items overall container

        Raises
        ======
        BaseASHException
            Invalid ignore ignore_type
        ValueError
            item is not of ignore_type int or int convertible

        Notes
        =====
        This will silently ignore any attempts
        to remove an item not ignored.
        """
        try:
            ignore_type = ignore_type.lower()
        except:
            raise ValueError("Expeced ignore_type of type: str")

        try:
            # TODO Handle more then just ints, take relevant objs as well
            if not isinstance(item, int):
                item = int(item)
        except ValueError:
            raise ValueError("Expected item of type: int")

        if ignore_type == "member":
            if item in self.options["ignore_users"]:
                index = self.options["ignore_users"].index(item)
                self.options["ignore_users"].pop(index)
        elif ignore_type == "channel":
            if item in self.options["ignore_channels"]:
                index = self.options["ignore_channels"].index(item)
                self.options["ignore_channels"].pop(index)
        elif ignore_type == "perm":
            if item in self.options["ignore_perms"]:
                index = self.options["ignore_perms"].index(item)
                self.options["ignore_perms"].pop(index)
        elif ignore_type == "guild":
            if item in self.options["ignore_guilds"]:
                index = self.options["ignore_guilds"].index(item)
                self.options["ignore_guilds"].pop(index)
        elif ignore_type == "role":
            if item in self.options["ignore_roles"]:
                index = self.options["ignore_roles"].index(item)
                self.options["ignore_roles"].pop(index)
        else:
            raise BaseASHException("Invalid ignore ignore_type")

        log.debug(f"Un-Ignored {ignore_type}: {item}")
Ejemplo n.º 2
0
    def add_ignored_item(self, item: int, ignore_type: str) -> None:
        """
        TODO Document this better with ignore_type notations
        Add an item to the relevant ignore list

        Parameters
        ----------
        item : int
            The id of the thing to ignore
        ignore_type : str
            A string representation of the ignored
            items overall container

        Raises
        ======
        BaseASHException
            Invalid ignore ignore_type
        ValueError
            item is not of ignore_type int or int convertible

        Notes
        =====
        This will silently ignore any attempts
        to add an item already added.
        """
        try:
            ignore_type = ignore_type.lower()
        except:
            raise ValueError("Expeced ignore_type of type: str")

        try:
            if not isinstance(item, int):
                item = int(item)
        except ValueError:
            raise ValueError("Expected item of type: int")

        if ignore_type == "member":
            if item not in self.options["ignore_users"]:
                self.options["ignore_users"].append(item)
        elif ignore_type == "channel":
            if item not in self.options["ignore_channels"]:
                self.options["ignore_channels"].append(item)
        elif ignore_type == "perm":
            if item not in self.options["ignore_perms"]:
                self.options["ignore_perms"].append(item)
        elif ignore_type == "guild":
            if item not in self.options["ignore_guilds"]:
                self.options["ignore_guilds"].append(item)
        elif ignore_type == "role":
            if item not in self.options["ignore_roles"]:
                self.options["ignore_roles"].append(item)
        else:
            raise BaseASHException("Invalid ignore ignore_type")

        log.debug(f"Ignored {ignore_type}: {item}")
Ejemplo n.º 3
0
    def get_guild_options(self, guild_id: int) -> tuple:
        """
        Get the options dictionary for a given guild,
        if the guild doesnt exist raise an exception

        Parameters
        ----------
        guild_id : int
            The guild to get custom options for

        Returns
        -------
        tuple
            The options for this guild as tuple[0] and tuple[1] is a bool
            which is used to say if the guild has custom options or not

            Be wary of the return value. It is in the format,
            (dict, boolean),
            where dict is the options and boolean is whether
            or not these options are custom

        Raises
        ------
        BaseASHException
            This guild does not exist

        Notes
        -----
        The value for tuple[1] is not checked/ensured at runtime.
        Be wary of this if you access guild and manually change
        options rather then using this libraries methods.

        Another thing to note is this returns a deepcopy of the
        options dictionary. This is to encourage usage of this
        libraries methods for changing options, rather then
        playing around with them yourself and potentially
        doing damage.

        """
        guild = Guild(self.bot, guild_id, self.options)
        try:
            guild = next(iter(g for g in self.guilds if g == guild))
        except StopIteration:
            raise BaseASHException("This guild does not exist")
        else:
            log.debug(f"Returned guild options for {guild_id}")
            return deepcopy(guild.options), guild.has_custom_options
Ejemplo n.º 4
0
    def AddIgnoredItem(self, item: int, type: str) -> None:
        """
        Add an item to the relevant ignore list

        Parameters
        ----------
        item : int
            The id of the thing to ignore
        type : str
            A string representation of the ignored
            items overall container

        Raises
        ======
        BaseASHException
            Invalid ignore type
        ValueError
            item is not of type int or int convertible

        Notes
        =====
        This will silently ignore any attempts
        to add an item already added.
        """
        type = type.lower()
        if not isinstance(item, int):
            item = int(item)

        if type == "user":
            if item not in self.options["ignoreUsers"]:
                self.options["ignoreUsers"].append(item)
        elif type == "channel":
            if item not in self.options["ignoreChannels"]:
                self.options["ignoreChannels"].append(item)
        elif type == "perm":
            if item not in self.options["ignorePerms"]:
                self.options["ignorePerms"].append(item)
        elif type == "guild":
            if item not in self.options["ignoreGuilds"]:
                self.options["ignoreGuilds"].append(item)
        elif type == "role":
            if item not in self.options["ignoreRoles"]:
                self.options["ignoreRoles"].append(item)
        else:
            raise BaseASHException("Invalid ignore type")

        self.logger.debug(f"Ignored {type}: {item}")
Ejemplo n.º 5
0
    def RemoveIgnoredItem(self, item: int, type: str) -> None:
        """
        Remove an item from the relevant ignore list

        Parameters
        ----------
        item : int
            The id of the thing to unignore
        type : str
            A string representation of the ignored
            items overall container

        Raises
        ======
        BaseASHException
            Invalid ignore type
        ValueError
            item is not of type int or int convertible

        Notes
        =====
        This will silently ignore any attempts
        to remove an item not ignored.
        """
        type = type.lower()
        if not isinstance(item, int):
            item = int(item)

        if type == "user":
            if item in self.options["ignoreUsers"]:
                index = self.options["ignoreUsers"].index(item)
                self.options["ignoreUsers"].pop(index)
        elif type == "channel":
            if item in self.options["ignoreChannels"]:
                index = self.options["ignoreChannels"].index(item)
                self.options["ignoreChannels"].pop(index)
        elif type == "perm":
            if item in self.options["ignorePerms"]:
                index = self.options["ignorePerms"].index(item)
                self.options["ignorePerms"].pop(index)
        else:
            raise BaseASHException("Invalid ignore type")

        self.logger.debug(f"Un-Ignored {type}: {item}")
Ejemplo n.º 6
0
    def _ensure_options(
        self,
        warn_threshold=None,
        kick_threshold=None,
        ban_threshold=None,
        message_interval=None,
        guild_warn_message=None,
        guild_kick_message=None,
        guild_ban_message=None,
        user_kick_message=None,
        user_ban_message=None,
        user_failed_kick_message=None,
        user_failed_ban_message=None,
        message_duplicate_count=None,
        message_duplicate_accuracy=None,
        delete_spam=None,
        ignore_perms=None,
        ignore_users=None,
        ignore_channels=None,
        ignore_roles=None,
        ignore_guilds=None,
        ignore_bots=None,
        warn_only=None,
        no_punish=None,
        per_channel_spam=None,
        guild_warn_message_delete_after=None,
        user_kick_message_delete_after=None,
        guild_kick_message_delete_after=None,
        user_ban_message_delete_after=None,
        guild_ban_message_delete_after=None,
    ):
        """
        Given the relevant arguments,
        validate and return the options dict

        Notes
        =====
        For args, view this class's __init__ docstring
        """
        if not isinstance(warn_threshold, int) and warn_threshold is not None:
            raise ValueError("Expected warn_threshold of type int")

        if not isinstance(kick_threshold, int) and kick_threshold is not None:
            raise ValueError("Expected kick_threshold of type int")

        if not isinstance(ban_threshold, int) and ban_threshold is not None:
            raise ValueError("Expected ban_threshold of type int")

        if not isinstance(message_interval,
                          int) and message_interval is not None:
            raise ValueError("Expected message_interval of type int")

        if message_interval is not None and message_interval < 1000:
            raise BaseASHException(
                "Minimum message_interval is 1 seconds (1000 ms)")

        if (not isinstance(guild_warn_message, (str, dict))
                and guild_warn_message is not None):
            raise ValueError("Expected guild_warn_message of type str or dict")

        if (not isinstance(guild_kick_message, (str, dict))
                and guild_kick_message is not None):
            raise ValueError("Expected guild_kick_message of type str or dict")

        if (not isinstance(guild_ban_message, (str, dict))
                and guild_ban_message is not None):
            raise ValueError("Expected guild_ban_message of type str or dict")

        if (not isinstance(user_kick_message, (str, dict))
                and user_kick_message is not None):
            raise ValueError("Expected user_kick_message of type str or dict")

        if (not isinstance(user_ban_message, (str, dict))
                and user_ban_message is not None):
            raise ValueError("Expected user_ban_message of type str or dict")

        if (not isinstance(user_failed_kick_message, (str, dict))
                and user_failed_kick_message is not None):
            raise ValueError(
                "Expected user_failed_kick_message of type str or dict")

        if (not isinstance(user_failed_ban_message, (str, dict))
                and user_failed_ban_message is not None):
            raise ValueError(
                "Expected user_failed_ban_message of type str or dict")

        if (not isinstance(message_duplicate_count, int)
                and message_duplicate_count is not None):
            raise ValueError("Expected message_duplicate_count of type int")

        # Convert message_duplicate_accuracy from int to float if exists
        if isinstance(message_duplicate_accuracy, int):
            message_duplicate_accuracy = float(message_duplicate_accuracy)
        if (not isinstance(message_duplicate_accuracy, float)
                and message_duplicate_accuracy is not None):
            raise ValueError(
                "Expected message_duplicate_accuracy of type float")
        if message_duplicate_accuracy is not None:
            if 1.0 > message_duplicate_accuracy or message_duplicate_accuracy > 100.0:
                # Only accept values between 1 and 100
                raise ValueError(
                    "Expected message_duplicate_accuracy between 1 and 100")

        if not isinstance(delete_spam, bool) and delete_spam is not None:
            raise ValueError("Expected delete_spam of type bool")

        if not isinstance(ignore_perms, list) and ignore_perms is not None:
            raise ValueError("Expected ignore_perms of type list")

        if not isinstance(ignore_users, list) and ignore_users is not None:
            raise ValueError("Expected ignore_users of type list")

        if not isinstance(ignore_channels,
                          list) and ignore_channels is not None:
            raise ValueError("Expected ignore_channels of type list")

        if not isinstance(ignore_roles, list) and ignore_roles is not None:
            raise ValueError("Expected ignore_roles of type list")

        if not isinstance(ignore_guilds, list) and ignore_guilds is not None:
            raise ValueError("Expected ignore_guilds of type list")

        if not isinstance(ignore_bots, bool) and ignore_bots is not None:
            raise ValueError("Expected ignore_bots of type bool")

        if not isinstance(warn_only, bool) and warn_only is not None:
            raise ValueError("Expected warn_only of type bool")

        if not isinstance(no_punish, bool) and no_punish is not None:
            raise ValueError("Expected no_punish of type bool")

        if not isinstance(per_channel_spam,
                          bool) and per_channel_spam is not None:
            raise ValueError("Expected per_channel_spam of type bool")

        if (not isinstance(guild_warn_message_delete_after, int)
                and guild_warn_message_delete_after is not None):
            raise ValueError(
                "Expected guild_warn_message_delete_after of type int")

        if (not isinstance(user_kick_message_delete_after, int)
                and user_kick_message_delete_after is not None):
            raise ValueError(
                "Expected user_kick_message_delete_after of type int")

        if (not isinstance(guild_kick_message_delete_after, int)
                and guild_kick_message_delete_after is not None):
            raise ValueError(
                "Expected guild_kick_message_delete_after of type int")

        if (not isinstance(user_ban_message_delete_after, int)
                and user_ban_message_delete_after is not None):
            raise ValueError(
                "Expected user_ban_message_delete_after of type int")

        if (not isinstance(guild_ban_message_delete_after, int)
                and guild_ban_message_delete_after is not None):
            raise ValueError(
                "Expected guild_ban_message_delete_after of type int")

        if warn_only and no_punish:
            raise BaseASHException(
                "Cannot do BOTH warn_only and no_punish. Pick one and try again"
            )

        # Now we have ignore_type checked everything, lets do some logic
        if ignore_bots is None:
            ignore_bots = Static.DEFAULTS.get("ignore_bots")

        if ignore_roles is not None:
            placeholder_ignore_roles = []
            for item in ignore_roles:
                if isinstance(item, discord.Role):
                    placeholder_ignore_roles.append(item.id)
                elif isinstance(item, int):
                    placeholder_ignore_roles.append(item)
                elif isinstance(item, str):
                    placeholder_ignore_roles.append(item)
                else:
                    raise ValueError(
                        "Expected discord.Role or int or str for ignore_roles")
            ignore_roles = placeholder_ignore_roles

        if ignore_channels is not None:
            placeholder_ignore_channels = []
            for item in ignore_channels:
                if isinstance(item, discord.TextChannel):
                    placeholder_ignore_channels.extend([item.id])
                else:
                    placeholder_ignore_channels.append(item)
            ignore_channels = placeholder_ignore_channels

        if ignore_users is not None:
            placeholder_ignore_users = []
            for item in ignore_users:
                if isinstance(item, discord.User) or isinstance(
                        item, discord.Member):
                    placeholder_ignore_users.append(item.id)
                else:
                    placeholder_ignore_users.append(item)
            ignore_users = placeholder_ignore_users

        return {
            "warn_threshold":
            warn_threshold or Static.DEFAULTS.get("warn_threshold"),
            "kick_threshold":
            kick_threshold or Static.DEFAULTS.get("kick_threshold"),
            "ban_threshold":
            ban_threshold or Static.DEFAULTS.get("ban_threshold"),
            "message_interval":
            message_interval or Static.DEFAULTS.get("message_interval"),
            "guild_warn_message":
            guild_warn_message or Static.DEFAULTS.get("guild_warn_message"),
            "guild_kick_message":
            guild_kick_message or Static.DEFAULTS.get("guild_kick_message"),
            "guild_ban_message":
            guild_ban_message or Static.DEFAULTS.get("guild_ban_message"),
            "user_kick_message":
            user_kick_message or Static.DEFAULTS.get("user_kick_message"),
            "user_ban_message":
            user_ban_message or Static.DEFAULTS.get("user_ban_message"),
            "user_failed_kick_message":
            user_failed_kick_message
            or Static.DEFAULTS.get("user_failed_kick_message"),
            "user_failed_ban_message":
            user_failed_ban_message
            or Static.DEFAULTS.get("user_failed_ban_message"),
            "message_duplicate_count":
            message_duplicate_count
            or Static.DEFAULTS.get("message_duplicate_count"),
            "message_duplicate_accuracy":
            message_duplicate_accuracy
            or Static.DEFAULTS.get("message_duplicate_accuracy"),
            "delete_spam":
            delete_spam or Static.DEFAULTS.get("delete_spam"),
            "ignore_perms":
            ignore_perms or Static.DEFAULTS.get("ignore_perms"),
            "ignore_users":
            ignore_users or Static.DEFAULTS.get("ignore_users"),
            "ignore_channels":
            ignore_channels or Static.DEFAULTS.get("ignore_channels"),
            "ignore_roles":
            ignore_roles or Static.DEFAULTS.get("ignore_roles"),
            "ignore_guilds":
            ignore_guilds or Static.DEFAULTS.get("ignore_guilds"),
            "ignore_bots":
            ignore_bots,
            "warn_only":
            warn_only or Static.DEFAULTS.get("warn_only"),
            "no_punish":
            no_punish or Static.DEFAULTS.get("no_punish"),
            "per_channel_spam":
            per_channel_spam or Static.DEFAULTS.get("per_channel_spam"),
            "guild_warn_message_delete_after":
            guild_warn_message_delete_after
            or Static.DEFAULTS.get("guild_warn_message_delete_after"),
            "user_kick_message_delete_after":
            user_kick_message_delete_after
            or Static.DEFAULTS.get("user_kick_message_delete_after"),
            "guild_kick_message_delete_after":
            guild_kick_message_delete_after
            or Static.DEFAULTS.get("guild_kick_message_delete_after"),
            "user_ban_message_delete_after":
            user_ban_message_delete_after
            or Static.DEFAULTS.get("user_ban_message_delete_after"),
            "guild_ban_message_delete_after":
            guild_ban_message_delete_after
            or Static.DEFAULTS.get("guild_ban_message_delete_after"),
        }
Ejemplo n.º 7
0
    def __init__(
        self,
        bot: commands.Bot,
        verboseLevel=0,
        *,
        warnThreshold=None,
        kickThreshold=None,
        banThreshold=None,
        messageInterval=None,
        warnMessage=None,
        kickMessage=None,
        banMessage=None,
        messageDuplicateCount=None,
        messageDuplicateAccuracy=None,
        ignorePerms=None,
        ignoreUsers=None,
        ignoreChannels=None,
        ignoreRoles=None,
        ignoreGuilds=None,
        ignoreBots=None,
    ):
        """
        This is the first initialization of the entire spam handler,
        this is also where the initial options are set

        Parameters
        ----------
        bot : commands.Bot
            The commands.Bot instance
        warnThreshold : int, optional
            This is the amount of messages in a row that result in a warning within the messageInterval
        kickThreshold : int, optional
            The amount of 'warns' before a kick occurs
        banThreshold : int, optional
            The amount of 'kicks' that occur before a ban occurs
        messageInterval : int, optional
            Amount of time a message is kept before being discarded.
            Essentially the amount of time (In milliseconds) a message can count towards spam
        warnMessage : str, optional
            The message to be sent upon warnThreshold being reached
        kickMessage : str, optional
            The message to be sent up kickThreshold being reached
        banMessage : str, optional
            The message to be sent up banThreshold being reached
        messageDuplicateCount : int, optional
            Amount of duplicate messages needed to trip a punishment
        messageDuplicateKick : int, optional
            Amount of duplicate messages needed within messageInterval to trip a kick
        messageDuplicateBan : int, optional
            Amount of duplicate messages needed within messageInterval to trip a ban
        messageDuplicateAccuracy : float, optional
            How 'close' messages need to be to be registered as duplicates (Out of 100)
        ignorePerms : list, optional
            The perms (ID Form), that bypass anti-spam
        ignoreUsers : list, optional
            The users (ID Form), that bypass anti-spam
        ignoreBots : bool, optional
            Should bots bypass anti-spam?
        """
        # Just gotta casually type check everything.
        if not isinstance(bot, commands.Bot):
            raise ValueError("Expected channel of type: commands.Bot")

        if not isinstance(verboseLevel, int):
            raise ValueError("Verbosity should be an int between 0-5")

        if not isinstance(warnThreshold, int) and warnThreshold is not None:
            raise ValueError("Expected warnThreshold of type: int")

        if not isinstance(kickThreshold, int) and kickThreshold is not None:
            raise ValueError("Expected kickThreshold of type: int")

        if not isinstance(banThreshold, int) and banThreshold is not None:
            raise ValueError("Expected banThreshold of type: int")

        if not isinstance(messageInterval,
                          int) and messageInterval is not None:
            raise ValueError("Expected messageInterval of type: int")

        if messageInterval is not None and messageInterval < 1000:
            raise BaseASHException(
                "Minimum messageInterval is 1 seconds (1000 ms)")

        if not isinstance(warnMessage, str) and warnMessage is not None:
            raise ValueError("Expected warnMessage of type: str")

        if not isinstance(kickMessage, str) and kickMessage is not None:
            raise ValueError("Expected kickMessage of type: str")

        if not isinstance(banMessage, str) and banMessage is not None:
            raise ValueError("Expected banMessage of type: str")

        if (not isinstance(messageDuplicateCount, int)
                and messageDuplicateCount is not None):
            raise ValueError("Expected messageDuplicateCount of type: int")

        if (not isinstance(messageDuplicateAccuracy, float)
                and messageDuplicateAccuracy is not None):
            raise ValueError("Expected messageDuplicateAccuracy of type: int")

        if messageDuplicateAccuracy is not None:
            if 1 > messageDuplicateAccuracy or messageDuplicateAccuracy > 100:
                # Only accept values between 1 and 100
                raise ValueError(
                    "Expected messageDuplicateAccuracy between 1 and 100")

        if not isinstance(ignorePerms, list) and ignorePerms is not None:
            raise ValueError("Expected ignorePerms of type: list")

        if not isinstance(ignoreUsers, list) and ignoreUsers is not None:
            raise ValueError("Expected ignoreUsers of type: list")

        if not isinstance(ignoreChannels, list) and ignoreChannels is not None:
            raise ValueError("Expected ignoreChannels of type: list")

        if not isinstance(ignoreRoles, list) and ignoreRoles is not None:
            raise ValueError("Expected ignoreRoles of type: list")

        if not isinstance(ignoreGuilds, list) and ignoreGuilds is not None:
            raise ValueError("Expected ignoreGuilds of type: list")

        if not isinstance(ignoreBots, bool) and ignoreBots is not None:
            raise ValueError("Expected ignoreBots of type: int")

        # Now we have type checked everything, lets do some logic
        if ignoreBots is None:
            ignoreBots = Static.DEFAULTS.get("ignoreBots")

        self.options = {
            "warnThreshold":
            warnThreshold or Static.DEFAULTS.get("warnThreshold"),
            "kickThreshold":
            kickThreshold or Static.DEFAULTS.get("kickThreshold"),
            "banThreshold":
            banThreshold or Static.DEFAULTS.get("banThreshold"),
            "messageInterval":
            messageInterval or Static.DEFAULTS.get("messageInterval"),
            "warnMessage":
            warnMessage or Static.DEFAULTS.get("warnMessage"),
            "kickMessage":
            kickMessage or Static.DEFAULTS.get("kickMessage"),
            "banMessage":
            banMessage or Static.DEFAULTS.get("banMessage"),
            "messageDuplicateCount":
            messageDuplicateCount
            or Static.DEFAULTS.get("messageDuplicateCount"),
            "messageDuplicateAccuracy":
            messageDuplicateAccuracy
            or Static.DEFAULTS.get("messageDuplicateAccuracy"),
            "ignorePerms":
            ignorePerms or Static.DEFAULTS.get("ignorePerms"),
            "ignoreUsers":
            ignoreUsers or Static.DEFAULTS.get("ignoreUsers"),
            "ignoreChannels":
            ignoreChannels or Static.DEFAULTS.get("ignoreChannels"),
            "ignoreRoles":
            ignoreRoles or Static.DEFAULTS.get("ignoreRoles"),
            "ignoreGuilds":
            ignoreGuilds or Static.DEFAULTS.get("ignoreGuilds"),
            "ignoreBots":
            ignoreBots,
        }

        self.bot = bot
        self._guilds = []

        logging.basicConfig(
            format="%(asctime)s | %(levelname)s | %(module)s | %(message)s",
            datefmt="%d/%m/%Y %I:%M:%S %p",
        )
        self.logger = logging.getLogger(__name__)
        if verboseLevel == 0:
            self.logger.setLevel(level=logging.NOTSET)
        elif verboseLevel == 1:
            self.logger.setLevel(level=logging.DEBUG)
        elif verboseLevel == 2:
            self.logger.setLevel(level=logging.INFO)
        elif verboseLevel == 3:
            self.logger.setLevel(level=logging.WARNING)
        elif verboseLevel == 4:
            self.logger.setLevel(level=logging.ERROR)
        elif verboseLevel == 5:
            self.logger.setLevel(level=logging.CRITICAL)