diff --git a/redbot/core/bot.py b/redbot/core/bot.py index 689379800..601820670 100644 --- a/redbot/core/bot.py +++ b/redbot/core/bot.py @@ -943,6 +943,7 @@ class RedBase( async def send_filtered( destination: discord.abc.Messageable, filter_mass_mentions=True, + filter_roles=True, filter_invite_links=True, filter_all_links=False, **kwargs, @@ -969,6 +970,8 @@ class RedBase( content = kwargs.pop("content", None) if content: + if filter_roles and isinstance(destination, discord.TextChannel): + content = common_filters.sanitize_role_mentions(content, destination.guild.roles) if filter_mass_mentions: content = common_filters.filter_mass_mentions(content) if filter_invite_links: diff --git a/redbot/core/commands/context.py b/redbot/core/commands/context.py index ebbb32c0f..3bcdc86e6 100644 --- a/redbot/core/commands/context.py +++ b/redbot/core/commands/context.py @@ -75,7 +75,10 @@ class Context(DPYContext): :func:`~redbot.core.utils.common_filters.filter_mass_mentions`. This must take a single `str` as an argument, and return the sanitized `str`. - \*\*kwargs + sanitize_roles : bool + Whether or not role mentions should be sanitized for you. + Defaults to ``True`` + **kwargs See `discord.ext.commands.Context.send`. Returns @@ -86,6 +89,10 @@ class Context(DPYContext): """ _filter = kwargs.pop("filter", common_filters.filter_mass_mentions) + sanitize_roles = kwargs.pop("sanitize_roles", True) + + if sanitize_roles and content and self.guild: + content = common_filters.sanitize_role_mentions(str(content), self.guild.roles) if _filter and content: content = _filter(str(content)) diff --git a/redbot/core/utils/common_filters.py b/redbot/core/utils/common_filters.py index 608a6be52..d03153972 100644 --- a/redbot/core/utils/common_filters.py +++ b/redbot/core/utils/common_filters.py @@ -1,4 +1,7 @@ import re +from typing import Iterable + +import discord __all__ = [ "URL_RE", @@ -11,6 +14,7 @@ __all__ = [ "normalize_smartquotes", "escape_spoilers", "escape_spoilers_and_mass_mentions", + "sanitize_role_mentions", ] # regexes @@ -173,3 +177,32 @@ def escape_spoilers_and_mass_mentions(content: str) -> str: The escaped string. """ return escape_spoilers(filter_mass_mentions(content)) + + +def sanitize_role_mentions(content: str, roles: Iterable[discord.Role]) -> str: + """ + Swaps out role mentions for @RoleName + + This should always be used prior to filtering everyone mentions + + Parameters + ---------- + content: str + The string to make substitutions to + roles: Iterable[discord.Role] + The roles to make substitutions for + + Returns + ------- + str + The resulting string + """ + transformations = {re.escape(fr"<@&{role.id}>"): f"@{role.name}" for role in roles} + + def repl(obj): + return transformations.get(re.escape(obj.group(0)), "") + + pattern = re.compile("|".join(transformations.keys())) + result = pattern.sub(repl, content) + + return result