aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Chris Goes <[email protected]>2019-01-09 21:28:13 -0700
committerGravatar GitHub <[email protected]>2019-01-09 21:28:13 -0700
commitd487e262e8e39388907ec137d878fdaf65d960ec (patch)
treefceaf287c1e6511fb951fb1a2135322075f9a7a0
parentMerge pull request #263 from python-discord/help-command-patch (diff)
parentMerge branch 'master' into SebastiaanZ/embed-detection-filter (diff)
Merge pull request #259 from python-discord/SebastiaanZ/embed-detection-filter
Detecting self-botting/unofficial client use with a rich embed filter
-rw-r--r--bot/cogs/filtering.py66
-rw-r--r--bot/cogs/modlog.py11
-rw-r--r--bot/constants.py2
-rw-r--r--config-default.yml18
4 files changed, 77 insertions, 20 deletions
diff --git a/bot/cogs/filtering.py b/bot/cogs/filtering.py
index 247ee26b8..570d6549f 100644
--- a/bot/cogs/filtering.py
+++ b/bot/cogs/filtering.py
@@ -45,6 +45,7 @@ class Filtering:
"enabled": Filter.filter_zalgo,
"function": self._has_zalgo,
"type": "filter",
+ "content_only": True,
"user_notification": Filter.notify_user_zalgo,
"notification_msg": (
"Your post has been removed for abusing Unicode character rendering (aka Zalgo text). "
@@ -55,6 +56,7 @@ class Filtering:
"enabled": Filter.filter_invites,
"function": self._has_invites,
"type": "filter",
+ "content_only": True,
"user_notification": Filter.notify_user_invites,
"notification_msg": (
f"Per Rule 10, your invite link has been removed. {_staff_mistake_str}\n\n"
@@ -65,20 +67,36 @@ class Filtering:
"enabled": Filter.filter_domains,
"function": self._has_urls,
"type": "filter",
+ "content_only": True,
"user_notification": Filter.notify_user_domains,
"notification_msg": (
f"Your URL has been removed because it matched a blacklisted domain. {_staff_mistake_str}"
)
},
+ "filter_rich_embeds": {
+ "enabled": Filter.filter_rich_embeds,
+ "function": self._has_rich_embed,
+ "type": "filter",
+ "content_only": False,
+ "user_notification": Filter.notify_user_rich_embeds,
+ "notification_msg": (
+ "Your post has been removed because it contained a rich embed. "
+ "This indicates that you're either using an unofficial discord client or are using a self-bot, "
+ f"both of which violate Discord's Terms of Service. {_staff_mistake_str}\n\n"
+ "Please don't use a self-bot or an unofficial Discord client on our server."
+ )
+ },
"watch_words": {
"enabled": Filter.watch_words,
"function": self._has_watchlist_words,
"type": "watchlist",
+ "content_only": True,
},
"watch_tokens": {
"enabled": Filter.watch_tokens,
"function": self._has_watchlist_tokens,
"type": "watchlist",
+ "content_only": True,
},
}
@@ -121,12 +139,35 @@ class Filtering:
# If none of the above, we can start filtering.
if filter_message:
for filter_name, _filter in self.filters.items():
-
# Is this specific filter enabled in the config?
if _filter["enabled"]:
- triggered = await _filter["function"](msg.content)
+ # Does the filter only need the message content or the full message?
+ if _filter["content_only"]:
+ triggered = await _filter["function"](msg.content)
+ else:
+ triggered = await _filter["function"](msg)
if triggered:
+ # If this is a filter (not a watchlist), we should delete the message.
+ if _filter["type"] == "filter":
+ try:
+ # Embeds (can?) trigger both the `on_message` and `on_message_edit`
+ # event handlers, triggering filtering twice for the same message.
+ #
+ # If `on_message`-triggered filtering already deleted the message
+ # then `on_message_edit`-triggered filtering will raise exception
+ # since the message no longer exists.
+ #
+ # In addition, to avoid sending two notifications to the user, the
+ # logs, and mod_alert, we return if the message no longer exists.
+ await msg.delete()
+ except discord.errors.NotFound:
+ return
+
+ # Notify the user if the filter specifies
+ if _filter["user_notification"]:
+ await self.notify_member(msg.author, _filter["notification_msg"], msg.channel)
+
if isinstance(msg.channel, DMChannel):
channel_str = "via DM"
else:
@@ -142,6 +183,8 @@ class Filtering:
log.debug(message)
+ additional_embeds = msg.embeds if filter_name == "filter_rich_embeds" else None
+
# Send pretty mod log embed to mod-alerts
await self.mod_log.send_log_message(
icon_url=Icons.filtering,
@@ -151,16 +194,9 @@ class Filtering:
thumbnail=msg.author.avatar_url_as(static_format="png"),
channel_id=Channels.mod_alerts,
ping_everyone=Filter.ping_everyone,
+ additional_embeds=additional_embeds,
)
- # If this is a filter (not a watchlist), we should delete the message.
- if _filter["type"] == "filter":
- await msg.delete()
-
- # Notify the user if the filter specifies
- if _filter["user_notification"]:
- await self.notify_member(msg.author, _filter["notification_msg"], msg.channel)
-
break # We don't want multiple filters to trigger
@staticmethod
@@ -272,6 +308,16 @@ class Filtering:
return True
return False
+ @staticmethod
+ async def _has_rich_embed(msg: Message):
+ """
+ Returns True if any of the embeds in the message
+ are of type 'rich', returns False otherwise
+ """
+ if msg.embeds:
+ return any(embed.type == "rich" for embed in msg.embeds)
+ return False
+
async def notify_member(self, filtered_member: Member, reason: str, channel: TextChannel):
"""
Notify filtered_member about a moderation action with the reason str
diff --git a/bot/cogs/modlog.py b/bot/cogs/modlog.py
index 0561b5afb..c96838a54 100644
--- a/bot/cogs/modlog.py
+++ b/bot/cogs/modlog.py
@@ -106,7 +106,7 @@ class ModLog:
async def send_log_message(
self, icon_url: Optional[str], colour: Colour, title: Optional[str], text: str,
thumbnail: str = None, channel_id: int = Channels.modlog, ping_everyone: bool = False,
- files: List[File] = None, content: str = None
+ files: List[File] = None, content: str = None, additional_embeds: List[Embed] = None,
):
embed = Embed(description=text)
@@ -125,7 +125,14 @@ class ModLog:
else:
content = "@everyone"
- await self.bot.get_channel(channel_id).send(content=content, embed=embed, files=files)
+ channel = self.bot.get_channel(channel_id)
+
+ await channel.send(content=content, embed=embed, files=files)
+
+ if additional_embeds:
+ await channel.send("With the following embed(s):")
+ for additional_embed in additional_embeds:
+ await channel.send(embed=additional_embed)
async def on_guild_channel_create(self, channel: GUILD_CHANNEL):
if channel.guild.id != GuildConstant.id:
diff --git a/bot/constants.py b/bot/constants.py
index bbe6c1604..c1375bb13 100644
--- a/bot/constants.py
+++ b/bot/constants.py
@@ -201,6 +201,7 @@ class Filter(metaclass=YAMLGetter):
filter_zalgo: bool
filter_invites: bool
filter_domains: bool
+ filter_rich_embeds: bool
watch_words: bool
watch_tokens: bool
@@ -208,6 +209,7 @@ class Filter(metaclass=YAMLGetter):
notify_user_zalgo: bool
notify_user_invites: bool
notify_user_domains: bool
+ notify_user_rich_embeds: bool
ping_everyone: bool
guild_invite_whitelist: List[int]
diff --git a/config-default.yml b/config-default.yml
index a51d00778..21d7f20b9 100644
--- a/config-default.yml
+++ b/config-default.yml
@@ -134,17 +134,19 @@ guild:
filter:
# What do we filter?
- filter_zalgo: false
- filter_invites: true
- filter_domains: true
- watch_words: true
- watch_tokens: true
+ filter_zalgo: false
+ filter_invites: true
+ filter_domains: true
+ filter_rich_embeds: true
+ watch_words: true
+ watch_tokens: true
# Notify user on filter?
# Notifications are not expected for "watchlist" type filters
- notify_user_zalgo: false
- notify_user_invites: true
- notify_user_domains: false
+ notify_user_zalgo: false
+ notify_user_invites: true
+ notify_user_domains: false
+ notify_user_rich_embeds: true
# Filter configuration
ping_everyone: true # Ping @everyone when we send a mod-alert?