from discord import Guild, Message from discord.ext import commands import re from datetime import timedelta from cogs.basecog import BaseCog from config import CONFIG from storage import Storage class URLSpamCog(BaseCog): CONFIG_KEY_EARLY_URL_TIMEOUT = "urlspam_early_url_timeout" CONFIG_KEY_EARLY_URL_ACTION = "urlspam_early_url_action" def __init__(self, bot): super().__init__(bot) def __early_url_timeout(self, guild: Guild) -> int: return Storage.get_config_value(guild, self.CONFIG_KEY_EARLY_URL_TIMEOUT) or \ self.get_cog_default('early_url_timeout') def __early_url_action(self, guild: Guild) -> str: return Storage.get_config_value(guild, self.CONFIG_KEY_EARLY_URL_ACTION) or \ self.get_cog_default('early_url_action') @commands.Cog.listener() async def on_message(self, message: Message): if message.guild is None or message.channel is None: # DM or something return action = self.__early_url_action(message.guild) if action == 'nothing': return if message.author.permissions_in(message.channel).ban_members: # Mods are exempt return if not self.__contains_url(message.content): return join_age = message.created_at - message.author.joined_at join_age_str = self.__format_timedelta(join_age) if join_age.total_seconds() < self.__early_url_timeout(message.guild): if action == 'modwarn': await self.warn(message.guild, f'User {message.author.mention} ' + f'posted a URL {join_age_str} after joining.\n\n> {message.content}') # TODO: Emoji actions elif action == 'delete': await message.delete() # TODO: Info to mods elif action == 'kick': await message.author.kick(reason=f'User posted a link {join_age_str} after joining') # TODO: Info to mods elif action == 'ban': await message.author.ban(reason=f'User posted a link {join_age_str} after joining', delete_message_days=1) # TODO: Info to mods def __contains_url(self, text: str) -> bool: p = re.compile(r'http[^\s]*') return p.search(text) is not None def __format_timedelta(self, timespan: timedelta) -> str: parts = [] d = timespan.days h = timespan.seconds // 3600 m = (timespan.seconds // 60) % 60 s = timespan.seconds % 60 if d > 0: parts.append(f'{d}d') if d > 0 or h > 0: parts.append(f'{h}h') if d > 0 or h > 0 or m > 0: parts.append(f'{m}m') parts.append(f'{s}s') # Limit the precision to the two most significant elements while len(parts) > 2: parts.pop(-1) return ' '.join(parts)