|
| 1 | +import discord |
| 2 | +import typing |
| 3 | +from discord.ext import commands |
| 4 | +from discord.ext.commands.context import Context |
| 5 | + |
| 6 | +import configs |
| 7 | +from .. import util, tasks |
| 8 | +from ..http.services import MuteService, BanService |
| 9 | +from ..strikes import Strike |
| 10 | + |
| 11 | +NO_REASON = "No reason specified" |
| 12 | + |
| 13 | + |
| 14 | +def parse_secs(value:int): |
| 15 | + return (value, "seconds" if value > 1 else "second", value) |
| 16 | + |
| 17 | + |
| 18 | +def parse_mins(value:int): |
| 19 | + total_time = value * 60 |
| 20 | + return (value, "minutes" if value > 1 else "minute", total_time) |
| 21 | + |
| 22 | + |
| 23 | +def parse_hours(value:int): |
| 24 | + total_time = value * 60 * 60 |
| 25 | + return (value, "hours" if value > 1 else "hour", total_time) |
| 26 | + |
| 27 | + |
| 28 | +def parse_days(value:int): |
| 29 | + total_time = value * 60 * 60 * 24 |
| 30 | + return (value, "days" if value > 1 else "day", total_time) |
| 31 | + |
| 32 | + |
| 33 | +def parse_week(value:int): |
| 34 | + total_time = value * 60 * 60 * 24 * 7 |
| 35 | + return (value, "weeks" if value > 1 else "week", total_time) |
| 36 | + |
| 37 | + |
| 38 | +class CustomTime(commands.Converter): |
| 39 | + async def convert(self, _, value): |
| 40 | + time_parse = value[-1].lower() |
| 41 | + try: |
| 42 | + int_value = int(value) |
| 43 | + except TypeError: |
| 44 | + int_value = int(value[:-1]) |
| 45 | + return { |
| 46 | + 's': parse_secs(int_value), |
| 47 | + 'm': parse_mins(int_value), |
| 48 | + 'h': parse_hours(int_value), |
| 49 | + 'd': parse_days(int_value), |
| 50 | + 'w': parse_week(int_value) |
| 51 | + }.get(time_parse, parse_secs(int_value)) |
| 52 | + |
| 53 | + |
| 54 | +class Moderation(commands.Cog): |
| 55 | + def __init__(self, bot: commands.Bot) -> None: |
| 56 | + self.mute_service = MuteService(bot.loop) |
| 57 | + self.ban_service = BanService(bot.loop) |
| 58 | + |
| 59 | + async def cog_check(self, ctx: Context) -> bool: |
| 60 | + return any([role in (configs.ADMIN_ROLE, configs.TRUST_ROLE) for role in ctx.author.roles]) |
| 61 | + |
| 62 | + @commands.command() |
| 63 | + @commands.has_any_role(configs.ADMIN_ROLE, configs.TRUST_ROLE) |
| 64 | + async def warn(self, ctx: Context, member: discord.Member, *, reason:str=NO_REASON): |
| 65 | + """ |
| 66 | + USAGE EXAMPLES: |
| 67 | + !warn @DeveloperAnonymous Is a noob |
| 68 | + """ |
| 69 | + |
| 70 | + warn_embed = discord.Embed(title=f"New case | Warning | {member}", color=15066368) |
| 71 | + warn_embed.add_field(name="User", value=member.mention) |
| 72 | + warn_embed.add_field(name="Moderator", value=ctx.author.mention) |
| 73 | + warn_embed.add_field(name="Reason", value=reason, inline=False) |
| 74 | + |
| 75 | + try: |
| 76 | + await member.send("You have been warned in %s: %s" % (ctx.guild.name, reason)) |
| 77 | + except(discord.errors.HTTPException, discord.errors.Forbidden): |
| 78 | + util.logger.warn("Failed to notify warn") |
| 79 | + |
| 80 | + await util.strike(member.id, str(Strike.WARN), reason) |
| 81 | + await util.say(configs.LOGS, embed=warn_embed) |
| 82 | + await util.react_to(ctx.message, u"\u2705") |
| 83 | + |
| 84 | + @commands.command() |
| 85 | + @commands.has_any_role(configs.ADMIN_ROLE, configs.TRUST_ROLE) |
| 86 | + async def mute(self, ctx: Context, member: discord.Member, length:typing.Optional[CustomTime]=-1, *, reason:str=NO_REASON): |
| 87 | + """ |
| 88 | + USAGE EXAMPLES: |
| 89 | + !mute @DeveloperAnonymous Is a noob |
| 90 | + !mute @DeveloperAnonymous 15 Is a noob |
| 91 | + !mute @DeveloperAnonymous 15s Is a noob |
| 92 | + !mute @DeveloperAnonymous 15m Is a noob |
| 93 | + !mute @DeveloperAnonymous 15h Is a noob |
| 94 | + !mute @DeveloperAnonymous 15d Is a noob |
| 95 | + !mute @DeveloperAnonymous 15w Is a noob |
| 96 | +
|
| 97 | + NOTE: |
| 98 | + s = second(s) |
| 99 | + m = minute(s) |
| 100 | + h = hour(s) |
| 101 | + d = day(s) |
| 102 | + w = week(s) |
| 103 | + """ |
| 104 | + if await util.has_role(member, ctx.guild.get_role(configs.MUTED_ROLE)): |
| 105 | + return await util.exception(ctx.channel, "This user is already muted!") |
| 106 | + |
| 107 | + mute_embed = discord.Embed(title=f"New case | Mute | {member}", color=15066368) |
| 108 | + mute_embed.add_field(name="User", value=member.mention) |
| 109 | + mute_embed.add_field(name="Moderator", value=ctx.author.mention) |
| 110 | + if type(length) == tuple: |
| 111 | + mute_embed.add_field(name="Length", value=f"{length[0]} {length[1]}") |
| 112 | + mute_embed.add_field(name="Reason", value=reason, inline=False) |
| 113 | + |
| 114 | + try: |
| 115 | + await member.send("You have been muted in %s: %s" % (ctx.guild.name, reason)) |
| 116 | + except (discord.errors.HTTPException, discord.errors.Forbidden): |
| 117 | + util.logger.warn("Failed to notify mute") |
| 118 | + |
| 119 | + seconds = length[2] if type(length) == tuple else None |
| 120 | + await tasks.create_mute_task(member, seconds) |
| 121 | + await util.strike(member.id, Strike.MUTE, reason) |
| 122 | + await self.mute_service.mute(member, reason, seconds) |
| 123 | + await util.say(configs.LOGS, embed=mute_embed) |
| 124 | + await util.react_to(ctx.message, u"\u2705") |
| 125 | + |
| 126 | + @commands.command() |
| 127 | + @commands.has_guild_permissions(manage_guild=True) |
| 128 | + async def unmute(self, ctx: Context, member: discord.Member, *, reason:str=NO_REASON): |
| 129 | + """ |
| 130 | + USAGE EXAMPLES: |
| 131 | + !unmute @DeveloperAnonymous |
| 132 | + !unmute @DeveloperAnonymous Is not a noob anymore |
| 133 | + """ |
| 134 | + if not await util.has_role(member, ctx.guild.get_role(configs.MUTED_ROLE)): |
| 135 | + return await util.exception(ctx.channel, "This user is not muted!") |
| 136 | + |
| 137 | + mute_embed = discord.Embed(title=f"New case | Unmute | {member}", color=15066368) |
| 138 | + mute_embed.add_field(name="User", value=member.mention) |
| 139 | + mute_embed.add_field(name="Reason", value=reason, inline=False) |
| 140 | + |
| 141 | + await util.unmute(member) |
| 142 | + await tasks.delete_task(member, Strike.MUTE, reason) |
| 143 | + await util.strike(member.id, Strike.UNMUTE, reason) |
| 144 | + await util.say(configs.LOGS, embed=mute_embed) |
| 145 | + await util.react_to(ctx.message, u"\u2705") |
| 146 | + |
| 147 | + @commands.command() |
| 148 | + @commands.has_guild_permissions(kick_members=True) |
| 149 | + async def kick(self, ctx: Context, member: discord.Member, *, reason:str=NO_REASON): |
| 150 | + """ |
| 151 | + USAGE EXAMPLES: |
| 152 | + !kick @DeveloperAnonymous |
| 153 | + !kick @DeveloperAnonymous Is a noob |
| 154 | + """ |
| 155 | + kick_embed = discord.Embed(title=f"New case | Kick | {member}", color=16758079) |
| 156 | + kick_embed.add_field(name="User", value=member.mention) |
| 157 | + kick_embed.add_field(name="Reason", value=reason, inline=False) |
| 158 | + |
| 159 | + try: |
| 160 | + await member.send("You have been kicked in %s: %s" % (ctx.guild.name, reason)) |
| 161 | + except (discord.errors.HTTPException, discord.errors.Forbidden): |
| 162 | + util.logger.warn("Failed to notify kick") |
| 163 | + |
| 164 | + await member.kick(reason=reason) |
| 165 | + await util.strike(member.id, Strike.KICK, reason) |
| 166 | + await util.say(configs.LOGS, embed=kick_embed) |
| 167 | + await util.react_to(ctx.message, u"\u2705") |
| 168 | + |
| 169 | + @commands.command() |
| 170 | + @commands.has_guild_permissions(ban_members=True) |
| 171 | + async def ban(self, ctx: Context, user: discord.User, *, reason:str=NO_REASON): |
| 172 | + """ |
| 173 | + USAGE EXAMPLES: |
| 174 | + !ban @DeveloperAnonymous |
| 175 | + !ban @DeveloperAnonymous Is a noob |
| 176 | + """ |
| 177 | + guild:discord.Guild = ctx.guild |
| 178 | + |
| 179 | + if user in [entry.user for entry in await guild.bans()]: |
| 180 | + return await util.exception(ctx.channel, "This user is already banned!") |
| 181 | + |
| 182 | + ban_embed = discord.Embed(title=f"New case | Ban | {user}", color=993326) |
| 183 | + ban_embed.add_field(name="User", value=user.mention) |
| 184 | + ban_embed.add_field(name="Reason", value=reason, inline=False) |
| 185 | + |
| 186 | + try: |
| 187 | + await user.send("You have been banned in %s: %s" % (ctx.guild.name, reason)) |
| 188 | + except (discord.errors.HTTPException, discord.errors.Forbidden): |
| 189 | + util.logger.warn("Failed to notify ban") |
| 190 | + |
| 191 | + await ctx.guild.ban(user, reason=reason) |
| 192 | + await util.strike(user.id, Strike.BAN, reason) |
| 193 | + await util.say(configs.LOGS, embed=ban_embed) |
| 194 | + await util.react_to(ctx.message, u"\u2705") |
| 195 | + |
| 196 | + @commands.command() |
| 197 | + @commands.has_guild_permissions(ban_members=True) |
| 198 | + async def unban(self, ctx: Context, user: discord.User, *, reason:str=NO_REASON): |
| 199 | + """ |
| 200 | + USAGE EXAMPLES: |
| 201 | + !unban @DeveloperAnonymous |
| 202 | + !unban @DeveloperAnonymous Is not a noob anymore |
| 203 | + """ |
| 204 | + guild:discord.Guild = ctx.guild |
| 205 | + |
| 206 | + if user not in [entry.user for entry in await guild.bans()]: |
| 207 | + return await util.exception(ctx.channel, "This user is not banned!") |
| 208 | + |
| 209 | + unban_embed = discord.Embed(title=f"New case | Unban | {user}", color=993326) |
| 210 | + unban_embed.add_field(name="User", value=user.mention) |
| 211 | + unban_embed.add_field(name="Reason", value=reason, inline=False) |
| 212 | + |
| 213 | + await guild.unban(user, reason=reason) |
| 214 | + await util.strike(user.id, Strike.UNBAN, reason) |
| 215 | + await util.say(configs.LOGS, embed=unban_embed) |
| 216 | + await util.react_to(ctx.message, u"\u2705") |
| 217 | + |
| 218 | + async def cog_command_error(self, ctx: Context, error): |
| 219 | + await util.say(ctx.channel, error) |
0 commit comments