diff options
| -rw-r--r-- | bot/cogs/moderation.py | 58 | 
1 files changed, 58 insertions, 0 deletions
diff --git a/bot/cogs/moderation.py b/bot/cogs/moderation.py index ac08d3dd4..d9c217b83 100644 --- a/bot/cogs/moderation.py +++ b/bot/cogs/moderation.py @@ -125,6 +125,11 @@ class Moderation(Scheduler):          :param reason: The reason for the kick.          """ +        if not await self.respect_role_hierarchy(ctx, user, 'kick'): +            # Ensure ctx author has a higher top role than the target user +            # Warning is sent to ctx by the helper method +            return +          notified = await self.notify_infraction(              user=user,              infr_type="Kick", @@ -171,6 +176,12 @@ class Moderation(Scheduler):          :param reason: The reason for the ban.          """ +        member = ctx.guild.get_member(user.id) +        if not await self.respect_role_hierarchy(ctx, member, 'ban'): +            # Ensure ctx author has a higher top role than the target user +            # Warning is sent to ctx by the helper method +            return +          notified = await self.notify_infraction(              user=user,              infr_type="Ban", @@ -327,6 +338,12 @@ class Moderation(Scheduler):          :param reason: The reason for the temporary ban.          """ +        member = ctx.guild.get_member(user.id) +        if not await self.respect_role_hierarchy(ctx, member, 'tempban'): +            # Ensure ctx author has a higher top role than the target user +            # Warning is sent to ctx by the helper method +            return +          notified = await self.notify_infraction(              user=user,              infr_type="Ban", @@ -420,6 +437,11 @@ class Moderation(Scheduler):          :param reason: The reason for the kick.          """ +        if not await self.respect_role_hierarchy(ctx, user, 'shadowkick'): +            # Ensure ctx author has a higher top role than the target user +            # Warning is sent to ctx by the helper method +            return +          response_object = await post_infraction(ctx, user, type="kick", reason=reason, hidden=True)          if response_object is None:              return @@ -456,6 +478,12 @@ class Moderation(Scheduler):          :param reason: The reason for the ban.          """ +        member = ctx.guild.get_member(user.id) +        if not await self.respect_role_hierarchy(ctx, member, 'shadowban'): +            # Ensure ctx author has a higher top role than the target user +            # Warning is sent to ctx by the helper method +            return +          response_object = await post_infraction(ctx, user, type="ban", reason=reason, hidden=True)          if response_object is None:              return @@ -581,6 +609,12 @@ class Moderation(Scheduler):          :param reason: The reason for the temporary ban.          """ +        member = ctx.guild.get_member(user.id) +        if not await self.respect_role_hierarchy(ctx, member, 'shadowtempban'): +            # Ensure ctx author has a higher top role than the target user +            # Warning is sent to ctx by the helper method +            return +          response_object = await post_infraction(ctx, user, type="ban", reason=reason, duration=duration, hidden=True)          if response_object is None:              return @@ -1207,6 +1241,30 @@ class Moderation(Scheduler):              if User in error.converters:                  await ctx.send(str(error.errors[0])) +    async def respect_role_hierarchy(self, ctx: Context, target: Member, infraction_type: str) -> bool: +        """ +        Check if the highest role of the invoking member is less than or equal to the target member + +        If this check fails, a warning is sent to the invoking ctx + +        Implement as a method rather than a check in order to avoid having to reimplement parameter +        checks & conversions in a dedicated check decorater +        """ + +        # Build role hierarchy +        actor = ctx.author +        role_hierarchy = {role: rank for rank, role in enumerate(reversed(ctx.guild.roles))} +        hierarchy_check = role_hierarchy[actor.top_role] <= role_hierarchy[target.top_role] + +        if not hierarchy_check: +            log.info( +                f"{actor} ({actor.id}) attempted to {infraction_type} " +                f"{target} ({target.id}), who has a higher top role" +            ) +            await ctx.send(f":x: {actor.mention}, you may not {infraction_type} someone with a higher top role") + +        return hierarchy_check +  def setup(bot):      bot.add_cog(Moderation(bot))  |