Skip to content

Commit

Permalink
huge update
Browse files Browse the repository at this point in the history
- allow detecting scaling from kubernetes
- switch db url loading to allow postgres migration
- fix censor event for anti-spam
- give flagging another emoji
- indicate edits for flagging/censoring
- filter archives for unauthorized messages
- anti spam management commands
- option for deleted message ids
- time indication for edit/delete
- add mmute
- clean for links and partial content
  • Loading branch information
AEnterprise committed Mar 5, 2021
1 parent 4680464 commit 9a0303b
Show file tree
Hide file tree
Showing 13 changed files with 526 additions and 223 deletions.
2 changes: 1 addition & 1 deletion GearBot/Bot/GearBot.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class GearBot(AutoShardedBot):

def __init__(self, *args, loop=None, **kwargs):
super().__init__(*args, loop=loop, **kwargs)
self.metrics = PromMonitors(self, kwargs.get("monitoring_prefix", ""))
self.metrics = PromMonitors(self, kwargs.get("monitoring_prefix", "gearbot"))
self.cluster = kwargs.get("cluster", 0)
self.total_shards = kwargs.get("shard_count", 1)
self.shard_ids = kwargs.get("shard_ids", [0])
Expand Down
3 changes: 2 additions & 1 deletion GearBot/Cogs/AntiSpam.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from discord.guild import Guild
from discord.member import Member
from discord.message import Message
from discord.utils import snowflake_time

from Bot import TheRealGearBot
from Cogs.BaseCog import BaseCog
Expand Down Expand Up @@ -322,7 +323,7 @@ async def censor_detector(self):
for b in buckets:
t = b["TYPE"]
if t == "censored":
msg_time = int(message.created_at.timestamp()) * 1000
msg_time = snowflake_time(message.id).time()
bucket = self.get_bucket(message.guild.id, f"censored:{count}", b, message.author.id)
if bucket is not None and await bucket.check(message.author.id, msg_time, 1, f"{message.channel.id}-{message.id}"):
count = await bucket.count(message.author.id, msg_time, expire=False)
Expand Down
44 changes: 26 additions & 18 deletions GearBot/Cogs/Censor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import re
from collections import namedtuple
from urllib import parse
from urllib.parse import urlparse

Expand All @@ -12,6 +13,7 @@
from Util.Matchers import INVITE_MATCHER, URL_MATCHER

EMOJI_REGEX = re.compile('([^<]*)<a?:(?:[^:]+):([0-9]+)>')
messageholder = namedtuple('censored_message', 'id author channel guild')
class Censor(BaseCog):

def __init__(self, bot):
Expand All @@ -25,7 +27,7 @@ async def on_message(self, message: discord.Message):
member = message.guild.get_member(message.author.id) #d.py is weird
if member is None:
return
await self.check_message(member, message.content, message.channel, message.id)
await self.check_message(member, message.content, message.channel, message.id, False)

@commands.Cog.listener()
async def on_raw_message_edit(self, event: discord.RawMessageUpdateEvent):
Expand All @@ -48,9 +50,9 @@ async def on_raw_message_edit(self, event: discord.RawMessageUpdateEvent):

member = await Utils.get_member(self.bot, channel.guild, author_id)
if member is not None and author_id != self.bot.user.id:
await self.check_message(member, event.data["content"], channel, event.message_id)
await self.check_message(member, event.data["content"], channel, event.message_id, True)

async def check_message(self, member, content, channel, message_id):
async def check_message(self, member, content, channel, message_id, edit):
if Permissioncheckers.get_user_lvl(member.guild, member) >= 2:
return
censorlist = Configuration.get_var(member.guild.id, "CENSORING", "TOKEN_CENSORLIST")
Expand All @@ -69,25 +71,25 @@ async def check_message(self, member, content, channel, message_id):
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)
await self.censor_invite(member, message_id, channel, code, "INVALID INVITE", content, edit)
return
if invite.guild is None:
await self.censor_invite(member, message_id, channel, code, "DM group", content)
await self.censor_invite(member, message_id, channel, code, "DM group", content, edit)
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)
await self.censor_invite(member, message_id, channel, code, invite.guild.name, content, edit)
return

content = content.lower()

if content in full_message_list:
await self.censor_message(message_id, content, channel, member, "", "_content")
await self.censor_message(message_id, content, channel, member, "", "_content", edit=edit)
return

for bad in (w.lower() for w in censorlist):
if bad in content:
await self.censor_message(message_id, content, channel, member, bad)
await self.censor_message(message_id, content, channel, member, bad, edit=edit)
return

if len(word_censorlist) > 0:
Expand All @@ -98,7 +100,7 @@ async def check_message(self, member, content, channel, message_id):
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")
await self.censor_message(message_id, content, channel, member, match[0], "_word", edit=edit)
return

if len(domain_list) > 0:
Expand All @@ -107,20 +109,21 @@ async def check_message(self, member, content, channel, message_id):
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")
await self.censor_message(message_id, content, channel, member, url.hostname, "_domain_blocked", edit=edit)
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")
await self.censor_message(message_id, content, channel, member, '', "_emoji_only", edit=edit)
return




async def censor_message(self, message_id, content, channel, member, bad, key=""):
async def censor_message(self, message_id, content, channel, member, bad, key="", edit=False):
e = '_edit' if edit else ''
if channel.permissions_for(channel.guild.me).manage_messages:
try:
self.bot.data["message_deletes"].add(message_id)
Expand All @@ -129,43 +132,48 @@ async def censor_message(self, message_id, content, channel, member, bad, key=""
pass
else:
clean_message = await Utils.clean(content, channel.guild, markdown=False)
GearbotLogging.log_key(channel.guild.id, f'censored_message{key}', user=member, user_id=member.id,
GearbotLogging.log_key(channel.guild.id, f'censored_message{key}{e}', user=member, user_id=member.id,
message=clean_message, sequence=bad, channel=channel.mention)
else:

clean_message = await Utils.clean(content, channel.guild, markdown=False)
GearbotLogging.log_key(channel.guild.id, f'censored_message_failed{key}', user=member,
GearbotLogging.log_key(channel.guild.id, f'censored_message_failed{key}{e}', user=member,
user_id=member.id, message=clean_message, sequence=bad,
link='https://discord.com/channels/{0}/{1}/{2}'.format(channel.guild.id, channel.id, message_id))
self.bot.dispatch("user_censored", messageholder(message_id, member, channel, channel.guild))

async def censor_invite(self, member, message_id, channel, code, server_name, content):
async def censor_invite(self, member, message_id, channel, code, server_name, content, edit):
# Allow for users with a trusted role, or trusted users, to post invite links
if Configuration.get_var(member.guild.id, "CENSORING", "ALLOW_TRUSTED_BYPASS") and Permissioncheckers.is_trusted(
member):
return

e = '_edit' if edit else ''

self.bot.data["message_deletes"].add(message_id)
clean_message = await Utils.clean(content, member.guild)
clean_name = Utils.clean_user(member)
try:
await channel.delete_messages([discord.Object(message_id)])
GearbotLogging.log_key(member.guild.id, 'censored_invite', user=clean_name, code=code, message=clean_message,
GearbotLogging.log_key(member.guild.id, f'censored_invite{e}', user=clean_name, code=code, message=clean_message,
server_name=server_name, user_id=member.id,
channel=channel.mention)
except discord.NotFound:
# we failed? guess we lost the race, log anyways
GearbotLogging.log_key(member.guild.id, 'invite_censor_fail', user=clean_name, code=code,
GearbotLogging.log_key(member.guild.id, f'invite_censor_fail{e}', user=clean_name, code=code,
message=clean_message, server_name=server_name, user_id=member.id,
channel=channel.mention)
if message_id in self.bot.data["message_deletes"]:
self.bot.data["message_deletes"].remove(message_id)
except discord.Forbidden:
GearbotLogging.log_key(member.guild.id, 'invite_censor_forbidden', user=clean_name, code=code,
GearbotLogging.log_key(member.guild.id, f'invite_censor_forbidden{e}', user=clean_name, code=code,
message=clean_message, server_name=server_name, user_id=member.id,
channel=channel.mention)
if message_id in self.bot.data["message_deletes"]:
self.bot.data["message_deletes"].remove(message_id)

self.bot.dispatch("user_censored", messageholder(message_id, member, channel, channel.guild))


def setup(bot):
bot.add_cog(Censor(bot))
9 changes: 7 additions & 2 deletions GearBot/Cogs/ModLog.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from discord.embeds import EmptyEmbed
from discord.ext import commands
from discord.raw_models import RawMessageDeleteEvent, RawMessageUpdateEvent
from discord.utils import snowflake_time

from Cogs.BaseCog import BaseCog
from Util import GearbotLogging, Configuration, Utils, Archive, Emoji, Translator, InfractionUtils, Features, \
Expand Down Expand Up @@ -89,7 +90,9 @@ async def on_raw_message_delete(self, data: RawMessageDeleteEvent):
return
channel = self.bot.get_channel(message.channel)
name = Utils.clean_user(user) if hasUser else str(message.author)
GearbotLogging.log_key(guild.id, 'message_removed', name=name, user_id=user.id if hasUser else 'WEBHOOK', channel=channel.mention)
time = Utils.to_pretty_time((datetime.datetime.utcnow() - snowflake_time(data.message_id)).total_seconds(), guild.id)
with_id = Configuration.get_var(guild.id, "MESSAGE_LOGS", "MESSAGE_ID")
GearbotLogging.log_key(guild.id, 'message_removed_with_id' if with_id else 'message_removed', name=name, user_id=user.id if hasUser else 'WEBHOOK', channel=channel.mention, message_id=data.message_id, time=time.strip())
type_string = None
if message.type is not None:
if message.type == MessageType.new_member.value:
Expand Down Expand Up @@ -182,7 +185,9 @@ async def on_raw_message_edit(self, event: RawMessageUpdateEvent):
if after is None or after == "":
after = f"<{Translator.translate('no_content', channel.guild.id)}>"
if hasUser and user.id not in Configuration.get_var(channel.guild.id, "MESSAGE_LOGS", "IGNORED_USERS") and user.id != channel.guild.me.id:
GearbotLogging.log_key(channel.guild.id, 'edit_logging', user=Utils.clean_user(user), user_id=user.id, channel=channel.mention)
time = Utils.to_pretty_time((datetime.datetime.utcnow() - snowflake_time(message.messageid)).total_seconds(), channel.guild.id)
with_id = Configuration.get_var(channel.guild.id, "MESSAGE_LOGS", "MESSAGE_ID")
GearbotLogging.log_key(channel.guild.id, 'edit_logging_with_id' if with_id else 'edit_logging', user=Utils.clean_user(user), user_id=user.id, channel=channel.mention, message_id=message.messageid, time=time)
if Configuration.get_var(channel.guild.id, "MESSAGE_LOGS", "EMBED"):
embed = discord.Embed()
embed.set_author(name=user if hasUser else message.author,
Expand Down
Loading

0 comments on commit 9a0303b

Please sign in to comment.