def __init__(self, bot): super(AntiSpam, self).__init__(bot) # store values as functions so only what is needed is computed self.generators = { "max_messages": lambda m: 1, "max_newlines": lambda m: len(m.content.split("\n")) - 1, "max_mentions": lambda m: len(MENTION_MATCHER.findall(m.content)), "max_links": lambda m: len(URL_MATCHER.findall(m.content)) } self.punishments = { "warn": self.warn_punishment, "mute": self.mute_punishment, "kick": self.kick_punishment, "temp_ban": self.temp_ban_punishment, "ban": self.ban_punishment } self.seriousness = { "warn": 1, "mute": 2, "kick": 3, "temp_ban": 4, "ban": 5 } self.extra_actions = WeakValueDictionary() self.processed = deque(maxlen=500) self.censor_processed = deque(maxlen=50) self.running = True bot.loop.create_task(self.censor_detector())
async def clean(text, guild: discord.Guild = None, markdown=True, links=True, emoji=True, lookalikes=True): text = str(text) if guild is not None: # resolve user mentions for uid in set(ID_MATCHER.findall(text)): name = "@" + await username(int(uid), False, False) text = text.replace(f"<@{uid}>", name) text = text.replace(f"<@!{uid}>", name) # resolve role mentions for uid in set(ROLE_ID_MATCHER.findall(text)): role = discord.utils.get(guild.roles, id=int(uid)) if role is None: name = "@UNKNOWN ROLE" else: name = "@" + role.name text = text.replace(f"<@&{uid}>", name) # resolve channel names for uid in set(CHANNEL_ID_MATCHER.findall(text)): channel = guild.get_channel(uid) if channel is None: name = "#UNKNOWN CHANNEL" else: name = "#" + channel.name text = text.replace(f"<@#{uid}>", name) # re-assemble emoji so such a way that they don't turn into twermoji urls = set(URL_MATCHER.findall(text)) if lookalikes: text = replace_lookalikes(text) if markdown: text = escape_markdown(text) else: text = text.replace("@", "@\u200b").replace("**", "**").replace("``", "``") if emoji: for e in set(EMOJI_MATCHER.findall(text)): a, b, c = zip(e) text = text.replace(f"<{a[0]}:{b[0]}:{c[0]}>", f"<{a[0]}\\:{b[0]}\\:{c[0]}>") if links: #find urls last so the < escaping doesn't break it for url in urls: text = text.replace(escape_markdown(url), f"<{url}>") return text
async def check_message(self, member, content, channel, message_id): if Permissioncheckers.get_user_lvl(member.guild, member) >= 2: return censorlist = Configuration.get_var(member.guild.id, "CENSORING", "TOKEN_CENSORLIST") word_censorlist = Configuration.get_var(member.guild.id, "CENSORING", "WORD_CENSORLIST") guilds = Configuration.get_var(member.guild.id, "CENSORING", "ALLOWED_INVITE_LIST") domain_list = Configuration.get_var(member.guild.id, "CENSORING", "DOMAIN_LIST") domains_allowed = Configuration.get_var(member.guild.id, "CENSORING", "DOMAIN_LIST_ALLOWED") full_message_list = Configuration.get_var(member.guild.id, "CENSORING", "FULL_MESSAGE_LIST") censor_emoji_message = Configuration.get_var( member.guild.id, "CENSORING", "CENSOR_EMOJI_ONLY_MESSAGES") content = content.replace('\\', '') decoded_content = parse.unquote(content) if len(guilds) is not 0: codes = INVITE_MATCHER.findall(decoded_content) for code in codes: try: invite: discord.Invite = await self.bot.fetch_invite(code) except discord.NotFound: await self.censor_invite(member, message_id, channel, code, "INVALID INVITE", content) return if invite.guild is None: await self.censor_invite(member, message_id, channel, code, "DM group", content) return else: if invite.guild is None or ( not invite.guild.id in guilds and invite.guild.id != member.guild.id): await self.censor_invite(member, message_id, channel, code, invite.guild.name, content) return content = content.lower() if content in full_message_list: await self.censor_message(message_id, content, channel, member, "", "_content") return for bad in (w.lower() for w in censorlist): if bad in content: await self.censor_message(message_id, content, channel, member, bad) return if len(word_censorlist) > 0: if channel.guild.id not in self.regexes: regex = re.compile( r"\b(" + '|'.join(re.escape(word) for word in word_censorlist) + r")\b", re.IGNORECASE) self.regexes[channel.guild.id] = regex else: regex = self.regexes[channel.guild.id] match = regex.findall(content) if len(match): await self.censor_message(message_id, content, channel, member, match[0], "_word") return if len(domain_list) > 0: link_list = URL_MATCHER.findall(content) for link in link_list: url = urlparse(link) domain = url.hostname if (domain in domain_list) is not domains_allowed: await self.censor_message(message_id, content, channel, member, url.hostname, "_domain_blocked") return if censor_emoji_message and content is not None and len(content) > 0: new_content = ''.join(c for c in content if c not in emoji.UNICODE_EMOJI) new_content = re.sub(EMOJI_REGEX, '', new_content) if new_content == '': await self.censor_message(message_id, content, channel, member, '', "_emoji_only") return
async def check_message(self, message: discord.Message): if message.guild is None or \ message.webhook_id is not None or \ message.author == message.guild.me: return ctx = await self.bot.get_context(message) if Permissioncheckers.get_user_lvl(ctx.guild, ctx.author) >= 2: return censorlist = Configuration.get_var(message.guild.id, "CENSORING", "TOKEN_CENSORLIST") word_censorlist = Configuration.get_var(message.guild.id, "CENSORING", "WORD_CENSORLIST") guilds = Configuration.get_var(message.guild.id, "CENSORING", "ALLOWED_INVITE_LIST") domain_list = Configuration.get_var(message.guild.id, "CENSORING", "DOMAIN_LIST") domains_allowed = Configuration.get_var(message.guild.id, "CENSORING", "DOMAIN_LIST_ALLOWED") content = message.content.replace('\\', '') decoded_content = parse.unquote(content) censored = False if len(guilds) is not 0: codes = INVITE_MATCHER.findall(decoded_content) for code in codes: try: invite: discord.Invite = await self.bot.fetch_invite(code) except discord.NotFound: await self.censor_invite(ctx, code, "INVALID INVITE") return if invite.guild is None: await self.censor_invite(ctx, code, "DM group") censored = True else: if invite.guild is None or ( not invite.guild.id in guilds and invite.guild.id != message.guild.id): await self.censor_invite(ctx, code, invite.guild.name) censored = True if not censored: content = content.lower() for bad in (w.lower() for w in censorlist): if bad in content: await self.censor_message(message, bad) censored = True break if not censored and len(word_censorlist) > 0: if ctx.guild.id not in self.regexes: regex = re.compile( r"\b(" + '|'.join(re.escape(word) for word in word_censorlist) + r")\b", re.IGNORECASE) self.regexes[ctx.guild.id] = regex else: regex = self.regexes[ctx.guild.id] match = regex.findall(message.content) if len(match): await self.censor_message(message, match[0], "_word") censored = True if not censored and len(domain_list) > 0: link_list = URL_MATCHER.findall(message.content) for link in link_list: url = urlparse(link) domain = url.hostname if (domain in domain_list) is not domains_allowed: await self.censor_message(message, url.hostname, "_domain_blocked") print(domain)
async def check_message(self, member, content, channel, message_id, edit, reply, attachments): if Permissioncheckers.get_user_lvl(member.guild, member) >= 2: return censorlist = Configuration.get_var(member.guild.id, "CENSORING", "TOKEN_CENSORLIST") word_censorlist = Configuration.get_var(member.guild.id, "CENSORING", "WORD_CENSORLIST") guilds = Configuration.get_var(member.guild.id, "CENSORING", "ALLOWED_INVITE_LIST") domain_list = Configuration.get_var(member.guild.id, "CENSORING", "DOMAIN_LIST") domains_allowed = Configuration.get_var(member.guild.id, "CENSORING", "DOMAIN_LIST_ALLOWED") full_message_list = Configuration.get_var(member.guild.id, "CENSORING", "FULL_MESSAGE_LIST") censor_emoji_message = Configuration.get_var( member.guild.id, "CENSORING", "CENSOR_EMOJI_ONLY_MESSAGES") content = content.replace('\\', '') if Configuration.get_var(member.guild.id, "CENSORING", "IGNORE_IDS"): content = re.sub(r'(<(?:@|#|@&|@!)[0-9]{15,20}>)', '', content) content = re.sub(r'<a?:[^: \n]+:([0-9]{15,20})>', '', content) content = re.sub( r"(https://(?:canary|ptb)?\.?discord(?:app)?.com/channels/\d{15,20}/\d{15,20}/\d{15,20})", '', content) decoded_content = parse.unquote(content) if len(guilds) != 0: codes = INVITE_MATCHER.findall(decoded_content) for code in codes: try: invite: disnake.Invite = await self.bot.fetch_invite(code) except disnake.NotFound: await self.censor_invite(member, message_id, channel, code, "INVALID INVITE", content, edit, reply, attachments) return if invite.guild is None: await self.censor_invite(member, message_id, channel, code, "DM group", content, edit, reply, attachments) return else: if invite.guild is None or ( not invite.guild.id in guilds and invite.guild.id != member.guild.id): await self.censor_invite(member, message_id, channel, code, invite.guild.name, content, edit, reply, attachments) return content = content.lower() if content in full_message_list: await self.censor_message(message_id, content, channel, member, "", "_content", edit=edit, reply=reply, attachments=attachments) return for bad in (w.lower() for w in censorlist): if bad in content: await self.censor_message(message_id, content, channel, member, bad, edit=edit, reply=reply, attachments=attachments) return if len(word_censorlist) > 0: if channel.guild.id not in self.regexes: regex = re.compile( r"(:?\b| )(" + '|'.join(re.escape(word) for word in word_censorlist) + r")(:?\b| )", re.IGNORECASE | re.MULTILINE) self.regexes[channel.guild.id] = regex else: regex = self.regexes[channel.guild.id] match = regex.findall(content) if len(match) > 0: m = match[0] if isinstance(m, tuple): m = m[1] await self.censor_message(message_id, content, channel, member, m, "_word", edit=edit, reply=reply, attachments=attachments) return if len(domain_list) > 0: link_list = URL_MATCHER.findall(content) for link in link_list: url = urlparse(link) domain = url.hostname if (domain in domain_list) is not domains_allowed: await self.censor_message(message_id, content, channel, member, url.hostname, "_domain_blocked", edit=edit, reply=reply, attachments=attachments) return if censor_emoji_message and content is not None and len(content) > 0: new_content = ''.join(c for c in content if c not in emoji.UNICODE_EMOJI) new_content = re.sub(EMOJI_REGEX, '', new_content) if new_content == '': await self.censor_message(message_id, content, channel, member, '', "_emoji_only", edit=edit, reply=reply, attachments=attachments) return