[Mod] Added optional role hierarchy check

Toggleable with [p]modset hierarchy
This enables a role hierarchy check before all moderation actions. If the mod doesn't have a role superior to the user's top role the action will be denied.
Server owner and bot owner are exempt from the check.
This commit is contained in:
Twentysix
2017-03-15 22:54:27 +01:00
parent 3d9a157516
commit 7c192b3668

View File

@@ -31,9 +31,10 @@ ACTIONS_CASES = {
} }
default_settings = { default_settings = {
"ban_mention_spam" : False, "ban_mention_spam" : False,
"delete_repeats" : False, "delete_repeats" : False,
"mod-log" : None "mod-log" : None,
"respect_hierarchy" : False
} }
@@ -111,14 +112,18 @@ class Mod:
await send_cmd_help(ctx) await send_cmd_help(ctx)
roles = settings.get_server(server).copy() roles = settings.get_server(server).copy()
_settings = {**self.settings[server.id], **roles} _settings = {**self.settings[server.id], **roles}
if "respect_hierarchy" not in _settings:
_settings["respect_hierarchy"] = default_settings["respect_hierarchy"]
if "delete_delay" not in _settings: if "delete_delay" not in _settings:
_settings["delete_delay"] = -1 _settings["delete_delay"] = "Disabled"
msg = ("Admin role: {ADMIN_ROLE}\n" msg = ("Admin role: {ADMIN_ROLE}\n"
"Mod role: {MOD_ROLE}\n" "Mod role: {MOD_ROLE}\n"
"Mod-log: {mod-log}\n" "Mod-log: {mod-log}\n"
"Delete repeats: {delete_repeats}\n" "Delete repeats: {delete_repeats}\n"
"Ban mention spam: {ban_mention_spam}\n" "Ban mention spam: {ban_mention_spam}\n"
"Delete delay: {delete_delay}\n" "Delete delay: {delete_delay}\n"
"Respects hierarchy: {respect_hierarchy}"
"".format(**_settings)) "".format(**_settings))
await self.bot.say(box(msg)) await self.bot.say(box(msg))
@@ -234,7 +239,7 @@ class Mod:
@modset.command(pass_context=True, no_pm=True, name='cases') @modset.command(pass_context=True, no_pm=True, name='cases')
async def set_cases(self, ctx, action: str = None, enabled: bool = None): async def set_cases(self, ctx, action: str = None, enabled: bool = None):
"""Enables or disables case creation for each type of mod action """Enables or disables case creation for each type of mod action
Enabled can be 'on' or 'off'""" Enabled can be 'on' or 'off'"""
server = ctx.message.server server = ctx.message.server
@@ -278,6 +283,23 @@ class Mod:
) )
await self.bot.say(msg) await self.bot.say(msg)
@modset.command(pass_context=True, no_pm=True)
@checks.serverowner_or_permissions()
async def hierarchy(self, ctx):
"""Toggles role hierarchy check for mods / admins"""
server = ctx.message.server
toggled = self.settings[server.id].get("respect_hierarchy",
default_settings["respect_hierarchy"])
if not toggled:
self.settings[server.id]["respect_hierarchy"] = True
await self.bot.say("Role hierarchy will be checked when "
"moderation commands are issued.")
else:
self.settings[server.id]["respect_hierarchy"] = False
await self.bot.say("Role hierarchy will be ignored when "
"moderation commands are issued.")
dataIO.save_json("data/mod/settings.json", self.settings)
@commands.command(no_pm=True, pass_context=True) @commands.command(no_pm=True, pass_context=True)
@checks.admin_or_permissions(kick_members=True) @checks.admin_or_permissions(kick_members=True)
async def kick(self, ctx, user: discord.Member, *, reason: str = None): async def kick(self, ctx, user: discord.Member, *, reason: str = None):
@@ -289,6 +311,11 @@ class Mod:
await self.bot.say("I cannot let you do that. Self-harm is " await self.bot.say("I cannot let you do that. Self-harm is "
"bad \N{PENSIVE FACE}") "bad \N{PENSIVE FACE}")
return return
elif not self.is_allowed_by_hierarchy(server, author, user):
await self.bot.say("I cannot let you do that. You are "
"not higher than the user in the role "
"hierarchy.")
return
try: try:
await self.bot.kick(user) await self.bot.kick(user)
@@ -319,6 +346,11 @@ class Mod:
await self.bot.say("I cannot let you do that. Self-harm is " await self.bot.say("I cannot let you do that. Self-harm is "
"bad \N{PENSIVE FACE}") "bad \N{PENSIVE FACE}")
return return
elif not self.is_allowed_by_hierarchy(server, author, user):
await self.bot.say("I cannot let you do that. You are "
"not higher than the user in the role "
"hierarchy.")
return
if days: if days:
if days.isdigit(): if days.isdigit():
@@ -365,6 +397,11 @@ class Mod:
await self.bot.say("I cannot let you do that. Self-harm is " await self.bot.say("I cannot let you do that. Self-harm is "
"bad \N{PENSIVE FACE}") "bad \N{PENSIVE FACE}")
return return
elif not self.is_allowed_by_hierarchy(server, author, user):
await self.bot.say("I cannot let you do that. You are "
"not higher than the user in the role "
"hierarchy.")
return
try: try:
invite = await self.bot.create_invite(server, max_age=3600*24) invite = await self.bot.create_invite(server, max_age=3600*24)
@@ -433,10 +470,17 @@ class Mod:
channel = ctx.message.channel channel = ctx.message.channel
server = ctx.message.server server = ctx.message.server
overwrites = channel.overwrites_for(user) overwrites = channel.overwrites_for(user)
if overwrites.send_messages is False: if overwrites.send_messages is False:
await self.bot.say("That user can't send messages in this " await self.bot.say("That user can't send messages in this "
"channel.") "channel.")
return return
elif not self.is_allowed_by_hierarchy(server, author, user):
await self.bot.say("I cannot let you do that. You are "
"not higher than the user in the role "
"hierarchy.")
return
self._perms_cache[user.id][channel.id] = overwrites.send_messages self._perms_cache[user.id][channel.id] = overwrites.send_messages
overwrites.send_messages = False overwrites.send_messages = False
try: try:
@@ -461,6 +505,13 @@ class Mod:
"""Mutes user in the server""" """Mutes user in the server"""
author = ctx.message.author author = ctx.message.author
server = ctx.message.server server = ctx.message.server
if not self.is_allowed_by_hierarchy(server, author, user):
await self.bot.say("I cannot let you do that. You are "
"not higher than the user in the role "
"hierarchy.")
return
register = {} register = {}
for channel in server.channels: for channel in server.channels:
if channel.type != discord.ChannelType.text: if channel.type != discord.ChannelType.text:
@@ -506,11 +557,20 @@ class Mod:
async def channel_unmute(self, ctx, user : discord.Member): async def channel_unmute(self, ctx, user : discord.Member):
"""Unmutes user in the current channel""" """Unmutes user in the current channel"""
channel = ctx.message.channel channel = ctx.message.channel
author = ctx.message.author
server = ctx.message.server
overwrites = channel.overwrites_for(user) overwrites = channel.overwrites_for(user)
if overwrites.send_messages: if overwrites.send_messages:
await self.bot.say("That user doesn't seem to be muted " await self.bot.say("That user doesn't seem to be muted "
"in this channel.") "in this channel.")
return return
elif not self.is_allowed_by_hierarchy(server, author, user):
await self.bot.say("I cannot let you do that. You are "
"not higher than the user in the role "
"hierarchy.")
return
if user.id in self._perms_cache: if user.id in self._perms_cache:
old_value = self._perms_cache[user.id].get(channel.id) old_value = self._perms_cache[user.id].get(channel.id)
else: else:
@@ -542,11 +602,19 @@ class Mod:
async def server_unmute(self, ctx, user : discord.Member): async def server_unmute(self, ctx, user : discord.Member):
"""Unmutes user in the server""" """Unmutes user in the server"""
server = ctx.message.server server = ctx.message.server
author = ctx.message.author
if user.id not in self._perms_cache: if user.id not in self._perms_cache:
await self.bot.say("That user doesn't seem to have been muted with {0}mute commands. " await self.bot.say("That user doesn't seem to have been muted with {0}mute commands. "
"Unmute them in the channels you want with `{0}unmute <user>`" "Unmute them in the channels you want with `{0}unmute <user>`"
"".format(ctx.prefix)) "".format(ctx.prefix))
return return
elif not self.is_allowed_by_hierarchy(server, author, user):
await self.bot.say("I cannot let you do that. You are "
"not higher than the user in the role "
"hierarchy.")
return
for channel in server.channels: for channel in server.channels:
if channel.type != discord.ChannelType.text: if channel.type != discord.ChannelType.text:
continue continue
@@ -1306,6 +1374,16 @@ class Mod:
else: else:
return False return False
def is_allowed_by_hierarchy(self, server, mod, user):
toggled = self.settings[server.id].get("respect_hierarchy",
default_settings["respect_hierarchy"])
is_special = mod == server.owner or mod.id == self.bot.settings.owner
if not toggled:
return True
else:
return mod.top_role.position > user.top_role.position or is_special
async def new_case(self, server, *, action, mod=None, user, reason=None, until=None, channel=None): async def new_case(self, server, *, action, mod=None, user, reason=None, until=None, channel=None):
action_type = action.lower() + "_cases" action_type = action.lower() + "_cases"
if not self.settings[server.id].get(action_type, default_settings[action_type]): if not self.settings[server.id].get(action_type, default_settings[action_type]):