Skip to content

Commit

Permalink
Role blocking, resolve #2753
Browse files Browse the repository at this point in the history
  • Loading branch information
fourjr committed Nov 5, 2020
1 parent 59eabf8 commit e7ba21b
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ however, insignificant breaking changes do not guarantee a major version bump, s
- Support Gyazo image links in message embeds. ([GH #282](https://github.com/kyb3r/modmail/issues/282))
- Added `silent` argument to `?contact` to restore old behaviour.
- If `?help` is sent, bot does checks on every command, `?help all` restores old behaviour. ([GH #2847](https://github.com/kyb3r/modmail/issues/2847))
- Added a way to block roles. ([GH #2753](https://github.com/kyb3r/modmail/issues/2753))

### Fixed

Expand Down
37 changes: 37 additions & 0 deletions bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,10 @@ def main_category(self) -> typing.Optional[discord.CategoryChannel]:
def blocked_users(self) -> typing.Dict[str, str]:
return self.config["blocked"]

@property
def blocked_roles(self) -> typing.Dict[str, str]:
return self.config["blocked_roles"]

@property
def blocked_whitelisted_users(self) -> typing.List[str]:
return self.config["blocked_whitelist"]
Expand Down Expand Up @@ -578,6 +582,36 @@ def check_guild_age(self, author: discord.Member) -> bool:
return False
return True

def check_manual_blocked_roles(self, author: discord.Member) -> bool:
for r in author.roles:
if str(r.id) in self.blocked_roles:

blocked_reason = self.blocked_roles.get(str(r.id)) or ""
now = datetime.utcnow()

# etc "blah blah blah... until 2019-10-14T21:12:45.559948."
end_time = re.search(r"until ([^`]+?)\.$", blocked_reason)
if end_time is None:
# backwards compat
end_time = re.search(r"%([^%]+?)%", blocked_reason)
if end_time is not None:
logger.warning(
r"Deprecated time message for user %s, block and unblock again to update.",
author.name,
)

if end_time is not None:
after = (datetime.fromisoformat(end_time.group(1)) - now).total_seconds()
if after <= 0:
# No longer blocked
self.blocked_users.pop(str(author.id))
logger.debug("No longer blocked, user %s.", author.name)
return True
logger.debug("User blocked, user %s.", author.name)
return False

return True

def check_manual_blocked(self, author: discord.Member) -> bool:
if str(author.id) not in self.blocked_users:
return True
Expand Down Expand Up @@ -656,6 +690,9 @@ async def is_blocked(
if not self.check_manual_blocked(author):
return True

if not self.check_manual_blocked_roles(author):
return True

await self.config.update()
return False

Expand Down
72 changes: 58 additions & 14 deletions cogs/modmail.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import discord
from discord.ext import commands
from discord.role import Role
from discord.utils import escape_markdown

from dateutil import parser
Expand Down Expand Up @@ -1009,6 +1010,7 @@ async def blocked(self, ctx):

embeds = [discord.Embed(title="Blocked Users", color=self.bot.main_color, description="")]

roles = []
users = []

for id_, reason in self.bot.blocked_users.items():
Expand All @@ -1022,6 +1024,11 @@ async def blocked(self, ctx):
except discord.NotFound:
users.append((id_, reason))

for id_, reason in self.bot.blocked_roles.items():
role = self.bot.guild.get_role(int(id_))
if role:
roles.append((role.mention, reason))

if users:
embed = embeds[0]

Expand All @@ -1039,7 +1046,29 @@ async def blocked(self, ctx):
else:
embeds[0].description = "Currently there are no blocked users."

embeds.append(
discord.Embed(title="Blocked Roles", color=self.bot.main_color, description="")
)

if roles:
embed = embeds[-1]

for mention, reason in roles:
line = mention + f" - {reason or 'No Reason Provided'}\n"
if len(embed.description) + len(line) > 2048:
embed = discord.Embed(
title="Blocked Roles (Continued)",
color=self.bot.main_color,
description=line,
)
embeds.append(embed)
else:
embed.description += line
else:
embeds[-1].description = "Currently there are no blocked roles."

session = EmbedPaginatorSession(ctx, *embeds)

await session.run()

@blocked.command(name="whitelist")
Expand Down Expand Up @@ -1100,7 +1129,13 @@ async def blocked_whitelist(self, ctx, *, user: User = None):
@commands.command(usage="[user] [duration] [reason]")
@checks.has_permissions(PermissionLevel.MODERATOR)
@trigger_typing
async def block(self, ctx, user: Optional[User] = None, *, after: UserFriendlyTime = None):
async def block(
self,
ctx,
user_or_role: Union[User, discord.Role] = None,
*,
after: UserFriendlyTime = None,
):
"""
Block a user from using Modmail.
Expand All @@ -1112,24 +1147,25 @@ async def block(self, ctx, user: Optional[User] = None, *, after: UserFriendlyTi
`duration` may be a simple "human-readable" time text. See `{prefix}help close` for examples.
"""

if user is None:
if user_or_role is None:
thread = ctx.thread
if thread:
user = thread.recipient
user_or_role = thread.recipient
elif after is None:
raise commands.MissingRequiredArgument(SimpleNamespace(name="user"))
else:
raise commands.BadArgument(f'User "{after.arg}" not found.')

mention = getattr(user, "mention", f"`{user.id}`")
mention = getattr(user_or_role, "mention", f"`{user_or_role.id}`")

if str(user.id) in self.bot.blocked_whitelisted_users:
embed = discord.Embed(
title="Error",
description=f"Cannot block {mention}, user is whitelisted.",
color=self.bot.error_color,
)
return await ctx.send(embed=embed)
if not isinstance(user_or_role, discord.Role):
if str(user_or_role.id) in self.bot.blocked_whitelisted_users:
embed = discord.Embed(
title="Error",
description=f"Cannot block {mention}, user is whitelisted.",
color=self.bot.error_color,
)
return await ctx.send(embed=embed)

reason = f"by {escape_markdown(ctx.author.name)}#{ctx.author.discriminator}"

Expand All @@ -1143,11 +1179,15 @@ async def block(self, ctx, user: Optional[User] = None, *, after: UserFriendlyTi

reason += "."

msg = self.bot.blocked_users.get(str(user.id))
if isinstance(user_or_role, discord.Role):
msg = self.bot.blocked_roles.get(str(user_or_role.id))
else:
msg = self.bot.blocked_users.get(str(user_or_role.id))

if msg is None:
msg = ""

if str(user.id) in self.bot.blocked_users and msg:
if msg:
old_reason = msg.strip().rstrip(".")
embed = discord.Embed(
title="Success",
Expand All @@ -1161,7 +1201,11 @@ async def block(self, ctx, user: Optional[User] = None, *, after: UserFriendlyTi
color=self.bot.main_color,
description=f"{mention} is now blocked {reason}",
)
self.bot.blocked_users[str(user.id)] = reason

if isinstance(user_or_role, discord.Role):
self.bot.blocked_roles[str(user_or_role.id)] = reason
else:
self.bot.blocked_users[str(user_or_role.id)] = reason
await self.bot.config.update()

return await ctx.send(embed=embed)
Expand Down
4 changes: 2 additions & 2 deletions cogs/utility.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ async def command_callback(self, ctx, *, command=None):
self.verify_checks = True
else:
self.verify_checks = False
if command == 'all':

if command == "all":
command = None

return await super().command_callback(ctx, command=command)
Expand Down
1 change: 1 addition & 0 deletions core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ class ConfigManager:
"oauth_whitelist": [],
# moderation
"blocked": {},
"blocked_roles": {},
"blocked_whitelist": [],
"command_permissions": {},
"level_permissions": {},
Expand Down

0 comments on commit e7ba21b

Please sign in to comment.