mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-05 18:58:53 -05:00
Add automodule reference for the antispam module (#5641)
Co-authored-by: Jakub Kuczys <me@jacken.men> Co-authored-by: Flame442 <34169552+Flame442@users.noreply.github.com>
This commit is contained in:
parent
3cb17116be
commit
1c7178a10b
@ -80,3 +80,9 @@ Utility UI
|
||||
|
||||
.. automodule:: redbot.core.utils.views
|
||||
:members:
|
||||
|
||||
AntiSpam
|
||||
========
|
||||
|
||||
.. automodule:: redbot.core.utils.antispam
|
||||
:members:
|
||||
|
||||
@ -2,21 +2,84 @@ from datetime import datetime, timedelta
|
||||
from typing import Tuple, List
|
||||
from collections import namedtuple
|
||||
|
||||
Interval = Tuple[timedelta, int]
|
||||
AntiSpamInterval = namedtuple("AntiSpamInterval", ["period", "frequency"])
|
||||
_AntiSpamInterval = namedtuple("_AntiSpamInterval", ["period", "frequency"])
|
||||
|
||||
|
||||
class AntiSpam:
|
||||
"""
|
||||
Custom class which is more flexible than using discord.py's
|
||||
`commands.cooldown()`
|
||||
A class that can be used to count the number of events that happened
|
||||
within specified intervals. Later, it can be checked whether the specified
|
||||
maximum count for any of the specified intervals has been exceeded.
|
||||
|
||||
Can be intialized with a custom set of intervals
|
||||
These should be provided as a list of tuples in the form
|
||||
(timedelta, quantity)
|
||||
Examples
|
||||
--------
|
||||
Tracking whether the number of reports sent by a user within a single guild is spammy:
|
||||
|
||||
Where quantity represents the maximum amount of times
|
||||
something should be allowed in an interval.
|
||||
.. code-block:: python
|
||||
|
||||
class MyCog(commands.Cog):
|
||||
INTERVALS = [
|
||||
# More than one report within last 5 seconds is considered spam.
|
||||
(datetime.timedelta(seconds=5), 1),
|
||||
# More than 3 reports within the last 5 minutes are considered spam.
|
||||
(datetime.timedelta(minutes=5), 3),
|
||||
# More than 10 reports within the last hour are considered spam.
|
||||
(datetime.timedelta(hours=1), 10),
|
||||
# More than 24 reports within a single day (last 24 hours) are considered spam.
|
||||
(datetime.timedelta(days=1), 24),
|
||||
]
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.antispam = {}
|
||||
|
||||
@commands.guild_only()
|
||||
@commands.command()
|
||||
async def report(self, ctx, content):
|
||||
# We want to track whether a single user within a single guild
|
||||
# sends a spammy number of reports.
|
||||
key = (ctx.guild.id, ctx.author.id)
|
||||
|
||||
if key not in self.antispam:
|
||||
# Create an instance of the AntiSpam class with given intervals.
|
||||
self.antispam[key] = AntiSpam(self.INTERVALS)
|
||||
# If you want to use the default intervals, you can use: AntiSpam([])
|
||||
|
||||
# Check if the user sent too many reports recently.
|
||||
# The `AntiSpam.spammy` property is `True` if, for any interval,
|
||||
# the number of events that happened within that interval
|
||||
# exceeds the number specified for that interval.
|
||||
if self.antispam[key].spammy:
|
||||
await ctx.send(
|
||||
"You've sent too many reports recently, please try again later."
|
||||
)
|
||||
return
|
||||
|
||||
# Make any other early-return checks.
|
||||
|
||||
# Record the event.
|
||||
self.antispam[key].stamp()
|
||||
|
||||
# Save the report.
|
||||
# self.config...
|
||||
|
||||
# Send a message to the user.
|
||||
await ctx.send("Your report has been submitted.")
|
||||
|
||||
Parameters
|
||||
----------
|
||||
intervals : List[Tuple[datetime.timedelta, int]]
|
||||
A list of tuples in the format (timedelta, int),
|
||||
where the timedelta represents the length of the interval,
|
||||
and the int represents the maximum number of times something
|
||||
can happen within that interval.
|
||||
|
||||
If an empty list is provided, the following defaults will be used:
|
||||
|
||||
* 3 per 5 seconds
|
||||
* 5 per 1 minute
|
||||
* 10 per 1 hour
|
||||
* 24 per 1 day
|
||||
"""
|
||||
|
||||
# TODO : Decorator interface for command check using `spammy`
|
||||
@ -30,13 +93,13 @@ class AntiSpam:
|
||||
(timedelta(days=1), 24),
|
||||
]
|
||||
|
||||
def __init__(self, intervals: List[Interval]):
|
||||
def __init__(self, intervals: List[Tuple[timedelta, int]]):
|
||||
self.__event_timestamps = []
|
||||
_itvs = intervals if intervals else self.default_intervals
|
||||
self.__intervals = [AntiSpamInterval(*x) for x in _itvs]
|
||||
_itvs = intervals or self.default_intervals
|
||||
self.__intervals = [_AntiSpamInterval(*x) for x in _itvs]
|
||||
self.__discard_after = max([x.period for x in self.__intervals])
|
||||
|
||||
def __interval_check(self, interval: AntiSpamInterval):
|
||||
def __interval_check(self, interval: _AntiSpamInterval):
|
||||
return (
|
||||
len([t for t in self.__event_timestamps if (t + interval.period) > datetime.utcnow()])
|
||||
>= interval.frequency
|
||||
@ -45,14 +108,18 @@ class AntiSpam:
|
||||
@property
|
||||
def spammy(self):
|
||||
"""
|
||||
use this to check if any interval criteria are met
|
||||
Whether, for any interval, the number of events that happened
|
||||
within that interval exceeds the number specified for that interval.
|
||||
"""
|
||||
return any(self.__interval_check(x) for x in self.__intervals)
|
||||
|
||||
def stamp(self):
|
||||
"""
|
||||
Use this to mark an event that counts against the intervals
|
||||
as happening now
|
||||
Mark an event timestamp against the list of antispam intervals happening right now.
|
||||
Use this after all checks have passed, and your action is taking place.
|
||||
|
||||
The stamp will last until the corresponding interval duration
|
||||
has expired (set when this AntiSpam object was initiated).
|
||||
"""
|
||||
self.__event_timestamps.append(datetime.utcnow())
|
||||
self.__event_timestamps = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user