mirror of
https://github.com/allthingslinux/tux.git
synced 2024-10-02 16:43:12 +00:00
Added a GIF ratelimiter cog.
Modified config example accordingly Modified constants accordingly
This commit is contained in:
parent
c9a526fc8d
commit
c07c086f3b
2 changed files with 128 additions and 5 deletions
|
@ -6,12 +6,11 @@ DEFAULT_PREFIX:
|
|||
|
||||
USER_IDS:
|
||||
SYSADMINS:
|
||||
- 123456789012345679
|
||||
- 123456789012345679
|
||||
BOT_OWNER: 123456789012345679
|
||||
- 123456789012345
|
||||
BOT_OWNER: 123456789012345
|
||||
|
||||
TEMPVC_CATEGORY_ID: 123456789012345679
|
||||
TEMPVC_CHANNEL_ID: 123456789012345679
|
||||
TEMPVC_CATEGORY_ID: 123456789012345
|
||||
TEMPVC_CHANNEL_ID: 123456789012345
|
||||
|
||||
EMBED_COLORS:
|
||||
DEFAULT: 16044058
|
||||
|
@ -40,3 +39,14 @@ EMBED_ICONS:
|
|||
KICK: "https://github.com/allthingslinux/tux/blob/main/assets/emojis/kick.png?raw=true"
|
||||
TIMEOUT: "https://github.com/allthingslinux/tux/blob/main/assets/emojis/timeout.png?raw=true"
|
||||
WARN: "https://github.com/allthingslinux/tux/blob/main/assets/emojis/warn.png?raw=true"
|
||||
|
||||
RECENT_GIF_AGE: 60
|
||||
|
||||
GIF_LIMIT_EXCLUDE:
|
||||
- 123456789012345
|
||||
|
||||
GIF_LIMITS_USER:
|
||||
"123456789012345": 2
|
||||
|
||||
GIF_LIMITS_CHANNEL:
|
||||
"123456789012345": 3
|
||||
|
|
113
tux/cogs/services/gif_limiter.py
Normal file
113
tux/cogs/services/gif_limiter.py
Normal file
|
@ -0,0 +1,113 @@
|
|||
from time import time
|
||||
from collections import defaultdict
|
||||
import asyncio
|
||||
from typing import DefaultDict
|
||||
|
||||
from tux.utils.constants import CONST
|
||||
from tux.bot import Tux
|
||||
|
||||
|
||||
import discord
|
||||
from discord.ext import tasks, commands
|
||||
from loguru import logger
|
||||
|
||||
# Helper function required as YAML keys are str. Channel and user IDs are int.
|
||||
def convert_dict_str_to_int(original_dict: dict[str, int]) -> dict[int, int]:
|
||||
|
||||
converted_dict:dict[int, int] = {}
|
||||
|
||||
for key, value in original_dict.items():
|
||||
try:
|
||||
int_key: int = int(key)
|
||||
converted_dict[int_key] = value
|
||||
except ValueError:
|
||||
logger.error("An error occurred when loading the GIF ratelimiter configuration.")
|
||||
|
||||
return converted_dict
|
||||
|
||||
class GifLimiter(commands.Cog):
|
||||
|
||||
"""
|
||||
This class is a handler for GIF ratelimiting.
|
||||
It keeps a list of GIF send times and routinely removes old times.
|
||||
If a user posts a GIF, the message_handler function should be externally called.
|
||||
It will delete the message if the user, channel or server-wide quota is exceeded.
|
||||
"""
|
||||
def __init__ (self, bot: Tux) -> None:
|
||||
self.bot = bot
|
||||
# Read config options and save them to local variables to avoid excessive reads
|
||||
# From the CONST[] dictionary
|
||||
self.recent_gif_age: int = CONST.RECENT_GIF_AGE # Max age for a GIF to be considered a recent post
|
||||
self.channelwide_gif_limits: dict[int, int] = convert_dict_str_to_int(CONST.GIF_LIMITS_CHANNEL) # Max GIFs sent recently for specific channels
|
||||
self.user_gif_limits: dict[int, int] = convert_dict_str_to_int(CONST.GIF_LIMITS) # Max recent GIFs sent by a user to be able to send a GIF in a channel
|
||||
self.gif_limit_exclude: list[int] = CONST.GIF_LIMIT_EXCLUDE # list of channels in which not to count GIFs
|
||||
|
||||
# Timestamps for recently-sent GIFs for the server, and channels
|
||||
self.recent_gifs_by_user: defaultdict[int, list[int]] = defaultdict(list) # UID, list of timestamps
|
||||
self.recent_gifs_by_channel: defaultdict[int, list[int]] = defaultdict(list) # Channel ID, list of timestamps
|
||||
self.recent_gifs_serverwide: list[int] = []
|
||||
|
||||
# Deletes the message passed as an argument, and creates a self-deleting message explaining the reason
|
||||
async def delete_message(self, message: discord.Message, epilogue: str) -> None:
|
||||
channel: discord.TextChannel = message.channel
|
||||
await message.delete()
|
||||
sent_message = await channel.send("-# GIF ratelimit exceeded " + epilogue)
|
||||
await asyncio.sleep(3)
|
||||
await sent_message.delete()
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_message(self, message: discord.Message) -> None:
|
||||
|
||||
# Nothing to do if the message doesn't have a .gif embed, or if it was sent in a blacklisted channel
|
||||
if (len(message.embeds) == 0
|
||||
or not "gif" in message.content.lower()
|
||||
or message.channel.id in self.gif_limit_exclude):
|
||||
return
|
||||
|
||||
channel: int = message.channel.id
|
||||
user: int = message.author.id
|
||||
|
||||
# Check if the message infringes on any ratelimits
|
||||
if (channel in self.channelwide_gif_limits
|
||||
and channel in self.recent_gifs_by_channel
|
||||
and len(self.recent_gifs_by_channel[channel]) >= self.channelwide_gif_limits[channel]):
|
||||
|
||||
await self.delete_message(message, "for channel")
|
||||
return
|
||||
|
||||
if (user in self.recent_gifs_by_user
|
||||
and channel in self.user_gif_limits
|
||||
and len(self.recent_gifs_by_user[user]) >= self.user_gif_limits[channel]):
|
||||
|
||||
await self.delete_message(message, "for user")
|
||||
return
|
||||
|
||||
# If it doesn't, add it to recent GIFs
|
||||
current_time: float = time()
|
||||
self.recent_gifs_by_channel[channel].append(current_time)
|
||||
self.recent_gifs_by_user[user].append(current_time)
|
||||
|
||||
# Function regularly cleans GIF lists and only keeps the most recent ones
|
||||
@tasks.loop(seconds=20)
|
||||
async def old_gif_remover(self) -> None:
|
||||
current_time: float = time()
|
||||
|
||||
for channel_id, timestamps in self.recent_gifs_by_channel.items():
|
||||
self.recent_gifs_by_channel[channel_id] = [
|
||||
t for t in timestamps
|
||||
if current_time - t < self.recent_gif_age
|
||||
]
|
||||
|
||||
|
||||
for user_id, timestamps in self.recent_gifs_by_user.items():
|
||||
self.recent_gifs_by_user[user_id] = [
|
||||
t for t in timestamps
|
||||
if current_time - t < self.recent_gif_age
|
||||
]
|
||||
|
||||
# Delete user key if no GIF has recently been sent by them
|
||||
if len(self.recent_gifs_by_user[user_id]) == 0:
|
||||
del self.recent_gifs_by_user[user_id]
|
||||
|
||||
async def setup(bot: Tux) -> None:
|
||||
await bot.add_cog(GifLimiter(bot))
|
Loading…
Reference in a new issue