| Total Complexity | 110 |
| Total Lines | 1118 |
| Duplicated Lines | 27.19 % |
| Changes | 0 | ||
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like moderation often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | import discord |
||
| 2 | import asyncio |
||
| 3 | from typing import Union |
||
| 4 | from discord.ext import commands |
||
| 5 | import variables as var |
||
| 6 | import database as db |
||
| 7 | from functions import get_prefix |
||
| 8 | from ext.permissions import has_command_permission |
||
| 9 | |||
| 10 | |||
| 11 | class Moderation(commands.Cog): |
||
| 12 | def __init__(self, bot): |
||
| 13 | self.bot = bot |
||
| 14 | |||
| 15 | View Code Duplication | async def cog_check(self, ctx): |
|
|
|
|||
| 16 | """Simple check to see if this cog (plugin) is enabled.""" |
||
| 17 | guild_doc = await db.PLUGINS.find_one({"_id": ctx.guild.id}) |
||
| 18 | |||
| 19 | if guild_doc.get("Moderation"): |
||
| 20 | return True |
||
| 21 | |||
| 22 | else: |
||
| 23 | await ctx.send( |
||
| 24 | embed=discord.Embed( |
||
| 25 | description=( |
||
| 26 | f"{var.E_DISABLE} The Moderation plugin " |
||
| 27 | "is disabled in this server" |
||
| 28 | ), |
||
| 29 | color=var.C_ORANGE |
||
| 30 | ) |
||
| 31 | ) |
||
| 32 | |||
| 33 | @commands.command() |
||
| 34 | @commands.has_permissions(ban_members=True) |
||
| 35 | @has_command_permission() |
||
| 36 | async def ban( |
||
| 37 | self, ctx, user: discord.User = None, *, reason="No reason given" |
||
| 38 | ): |
||
| 39 | if user is not None and user != ctx.author: |
||
| 40 | |||
| 41 | await ctx.guild.ban(user, reason=reason) |
||
| 42 | await ctx.send(f"Applied ban to `{user}` :ok_hand:") |
||
| 43 | |||
| 44 | try: |
||
| 45 | await user.send( |
||
| 46 | embed=discord.Embed( |
||
| 47 | title=f"You have been banned from {ctx.guild.name}", |
||
| 48 | description=( |
||
| 49 | "Sorry I'm just a bot and I follow orders :(" |
||
| 50 | ), |
||
| 51 | color=var.C_RED |
||
| 52 | ).add_field( |
||
| 53 | name="Reason", |
||
| 54 | value=reason |
||
| 55 | ).add_field( |
||
| 56 | name="Banned by", value=ctx.author |
||
| 57 | ) |
||
| 58 | ) |
||
| 59 | |||
| 60 | except discord.Forbidden: |
||
| 61 | pass |
||
| 62 | |||
| 63 | guild_log_doc = await db.LOGGING.find_one( |
||
| 64 | {"_id": ctx.guild.id} |
||
| 65 | ) |
||
| 66 | if guild_log_doc["modlog"]: |
||
| 67 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 68 | |||
| 69 | await channel.send(embed=discord.Embed( |
||
| 70 | title="🔨 Ban", |
||
| 71 | description=f"{user.mention} has been banned by {ctx.author.mention}", |
||
| 72 | color=var.C_GREEN |
||
| 73 | ).add_field( |
||
| 74 | name="Reason", |
||
| 75 | value=reason |
||
| 76 | ) |
||
| 77 | ) |
||
| 78 | |||
| 79 | |||
| 80 | |||
| 81 | elif user == ctx.author: |
||
| 82 | await ctx.send("You can't ban yourself :eyes:") |
||
| 83 | |||
| 84 | else: |
||
| 85 | await ctx.send( |
||
| 86 | embed=discord.Embed( |
||
| 87 | description=( |
||
| 88 | "🚫 You need to define the user to ban them," |
||
| 89 | " reason is optional" |
||
| 90 | ), |
||
| 91 | color=var.C_RED |
||
| 92 | ).add_field( |
||
| 93 | name="Format", |
||
| 94 | value=f"`{await get_prefix(ctx)}ban <user> <reason>`" |
||
| 95 | ).set_footer( |
||
| 96 | text="For user either User mention or User ID can be used") |
||
| 97 | ) |
||
| 98 | |||
| 99 | @ban.error |
||
| 100 | async def ban_error(self, ctx, error): |
||
| 101 | if isinstance(error, commands.CommandInvokeError): |
||
| 102 | await ctx.send( |
||
| 103 | embed=discord.Embed( |
||
| 104 | title="Permission error", |
||
| 105 | description=( |
||
| 106 | "🚫 I don't have permissions to ban the user, " |
||
| 107 | "make sure that my I have ban members permission" |
||
| 108 | " and role is placed above the highest role which" |
||
| 109 | " the user has" |
||
| 110 | ), |
||
| 111 | color=var.C_RED |
||
| 112 | ) |
||
| 113 | ) |
||
| 114 | |||
| 115 | @commands.command() |
||
| 116 | @commands.has_permissions(ban_members=True) |
||
| 117 | @has_command_permission() |
||
| 118 | async def unban(self, ctx, user: discord.User = None): |
||
| 119 | if user is not None: |
||
| 120 | bans = await ctx.guild.bans() |
||
| 121 | banned_users = [ban.user for ban in bans] |
||
| 122 | |||
| 123 | if user in banned_users: |
||
| 124 | await ctx.guild.unban(user) |
||
| 125 | await ctx.send(f'Successfully unbanned `{user}` :ok_hand:') |
||
| 126 | |||
| 127 | try: |
||
| 128 | await user.send( |
||
| 129 | embed=discord.Embed( |
||
| 130 | title=( |
||
| 131 | f"You have been unbanned from {ctx.guild.name}!" |
||
| 132 | ), |
||
| 133 | description="Yay I would be happy to see you back!", |
||
| 134 | color=var.C_GREEN |
||
| 135 | ).add_field( |
||
| 136 | name="Unbanned by", value=ctx.author) |
||
| 137 | ) |
||
| 138 | |||
| 139 | except discord.Forbidden: |
||
| 140 | pass |
||
| 141 | |||
| 142 | guild_log_doc = await db.LOGGING.find_one( |
||
| 143 | {"_id": ctx.guild.id} |
||
| 144 | ) |
||
| 145 | if guild_log_doc["modlog"]: |
||
| 146 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 147 | |||
| 148 | await channel.send(embed=discord.Embed( |
||
| 149 | title="🔨 Unban", |
||
| 150 | description=f"{user.mention} has been unbanned by {ctx.author.mention}", |
||
| 151 | color=var.C_BLUE |
||
| 152 | ) |
||
| 153 | ) |
||
| 154 | |||
| 155 | else: |
||
| 156 | await ctx.send( |
||
| 157 | embed=discord.Embed( |
||
| 158 | description=( |
||
| 159 | f"The user `{user}` is not banned, " |
||
| 160 | "therefore cannot unban them." |
||
| 161 | ), |
||
| 162 | color=var.C_ORANGE |
||
| 163 | ) |
||
| 164 | ) |
||
| 165 | |||
| 166 | else: |
||
| 167 | await ctx.send( |
||
| 168 | embed=discord.Embed( |
||
| 169 | description="🚫 You need to define the user to unban them", |
||
| 170 | color=var.C_RED |
||
| 171 | ).add_field( |
||
| 172 | name="Format", |
||
| 173 | value=f"`{await get_prefix(ctx)}unban <user>`" |
||
| 174 | ).set_footer( |
||
| 175 | text="For user either User mention or User ID can be used") |
||
| 176 | ) |
||
| 177 | |||
| 178 | async def unban_error(self, ctx, error): |
||
| 179 | if isinstance(error, commands.CommandInvokeError): |
||
| 180 | await ctx.send( |
||
| 181 | embed=discord.Embed( |
||
| 182 | title="Permission error", |
||
| 183 | description=( |
||
| 184 | "🚫 I don't have permissions to unban the user, make " |
||
| 185 | "sure that I have ban members permission and my role " |
||
| 186 | "is placed above the highest role which the user has" |
||
| 187 | ), |
||
| 188 | color=var.C_RED |
||
| 189 | ) |
||
| 190 | ) |
||
| 191 | |||
| 192 | @commands.command() |
||
| 193 | @commands.has_permissions(manage_roles=True) |
||
| 194 | @has_command_permission() |
||
| 195 | async def mute(self, ctx, member: discord.Member = None): |
||
| 196 | if member is not None: |
||
| 197 | if not discord.utils.get(ctx.guild.roles, name='Muted'): |
||
| 198 | muted_role = await ctx.guild.create_role( |
||
| 199 | name="Muted", colour=discord.Colour(0xa8a8a8) |
||
| 200 | ) |
||
| 201 | |||
| 202 | for i in ctx.guild.text_channels: |
||
| 203 | await i.set_permissions(muted_role, send_messages=False) |
||
| 204 | |||
| 205 | else: |
||
| 206 | muted_role = discord.utils.get(ctx.guild.roles, name="Muted") |
||
| 207 | |||
| 208 | await member.add_roles(muted_role) |
||
| 209 | await ctx.send(f"Applied chat mute to `{member}` :mute:") |
||
| 210 | |||
| 211 | guild_log_doc = await db.LOGGING.find_one( |
||
| 212 | {"_id": ctx.guild.id} |
||
| 213 | ) |
||
| 214 | if guild_log_doc["modlog"]: |
||
| 215 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 216 | |||
| 217 | await channel.send(embed=discord.Embed( |
||
| 218 | title="🔈 Mute", |
||
| 219 | description=f"{member.mention} has been muted by {ctx.author.mention}", |
||
| 220 | color=var.C_GREEN |
||
| 221 | ) |
||
| 222 | ) |
||
| 223 | else: |
||
| 224 | await ctx.send( |
||
| 225 | embed=discord.Embed( |
||
| 226 | description=( |
||
| 227 | "🚫 You need to define member in order to mute them" |
||
| 228 | ), |
||
| 229 | color=var.C_RED |
||
| 230 | ).add_field( |
||
| 231 | name="Format", |
||
| 232 | value=f"`{await get_prefix(ctx)}mute <member>`" |
||
| 233 | ).set_footer( |
||
| 234 | text=( |
||
| 235 | "For user either Member mention " |
||
| 236 | "or Member ID can be used" |
||
| 237 | ) |
||
| 238 | ) |
||
| 239 | ) |
||
| 240 | |||
| 241 | @mute.error |
||
| 242 | async def mute_error(self, ctx, error): |
||
| 243 | if isinstance(error, discord.Forbidden): |
||
| 244 | await ctx.send( |
||
| 245 | embed=discord.Embed( |
||
| 246 | title="Permission error", |
||
| 247 | description=( |
||
| 248 | "🚫 I don't have permissions to mute the member, make" |
||
| 249 | " sure that I have manage roles permission and my role" |
||
| 250 | " is placed above the highest role which the member has" |
||
| 251 | ), |
||
| 252 | color=var.C_RED |
||
| 253 | ) |
||
| 254 | ) |
||
| 255 | |||
| 256 | @commands.command() |
||
| 257 | @commands.has_permissions(manage_roles=True) |
||
| 258 | @has_command_permission() |
||
| 259 | async def unmute(self, ctx, member: discord.Member = None): |
||
| 260 | if member is None: |
||
| 261 | await ctx.send( |
||
| 262 | embed=discord.Embed( |
||
| 263 | description=( |
||
| 264 | "🚫 You need to define the member to unmute them" |
||
| 265 | ), |
||
| 266 | color=var.C_RED |
||
| 267 | ).add_field( |
||
| 268 | name="Format", |
||
| 269 | value=f"`{await get_prefix(ctx)}unmute <member>`" |
||
| 270 | ).set_footer( |
||
| 271 | text=( |
||
| 272 | "For user either Member mention " |
||
| 273 | "or Member ID can be used" |
||
| 274 | ) |
||
| 275 | ) |
||
| 276 | ) |
||
| 277 | elif not discord.utils.get(ctx.guild.roles, name='Muted'): |
||
| 278 | await ctx.send( |
||
| 279 | "There is no muted role yet hence I cannot unmute, " |
||
| 280 | "Muting someone automatically makes one." |
||
| 281 | ) |
||
| 282 | |||
| 283 | else: |
||
| 284 | muted_role = discord.utils.get(ctx.guild.roles, name='Muted') |
||
| 285 | |||
| 286 | await member.remove_roles(muted_role) |
||
| 287 | await ctx.send(f"Unmuted `{member}` :sound:") |
||
| 288 | |||
| 289 | guild_log_doc = await db.LOGGING.find_one( |
||
| 290 | {"_id": ctx.guild.id} |
||
| 291 | ) |
||
| 292 | if guild_log_doc["modlog"]: |
||
| 293 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 294 | |||
| 295 | await channel.send(embed=discord.Embed( |
||
| 296 | title="🔈 Unmute", |
||
| 297 | description=f"{member.mention} has been unmuted by {ctx.author.mention}", |
||
| 298 | color=var.C_BLUE |
||
| 299 | ) |
||
| 300 | ) |
||
| 301 | |||
| 302 | @unmute.error |
||
| 303 | async def unmute_error(self, ctx, error): |
||
| 304 | if isinstance(error, commands.CommandInvokeError): |
||
| 305 | await ctx.send( |
||
| 306 | embed=discord.Embed( |
||
| 307 | title="Permission error", |
||
| 308 | description=( |
||
| 309 | "🚫 I don't have permissions to unmute the user, " |
||
| 310 | "make sure that I have manage roles permission and " |
||
| 311 | "my role is placed above the highest role which the " |
||
| 312 | "user has" |
||
| 313 | ), |
||
| 314 | color=var.C_RED |
||
| 315 | ) |
||
| 316 | ) |
||
| 317 | |||
| 318 | @commands.command() |
||
| 319 | @commands.has_permissions(kick_members=True) |
||
| 320 | @has_command_permission() |
||
| 321 | async def kick( |
||
| 322 | self, ctx, member: discord.Member = None, *, reason="No reason provided" |
||
| 323 | ): |
||
| 324 | if member is not None and member != ctx.author: |
||
| 325 | await member.kick(reason=reason) |
||
| 326 | await ctx.send(f"`{member}` have been kicked from the server") |
||
| 327 | |||
| 328 | try: |
||
| 329 | await member.send( |
||
| 330 | embed=discord.Embed( |
||
| 331 | title=f"You have been kicked from {ctx.guild.name}", |
||
| 332 | color=var.C_RED |
||
| 333 | ).add_field( |
||
| 334 | name="Reason", value=reason |
||
| 335 | ).add_field( |
||
| 336 | name="Kicked by", value=ctx.author |
||
| 337 | ) |
||
| 338 | ) |
||
| 339 | |||
| 340 | except discord.Forbidden: |
||
| 341 | pass |
||
| 342 | |||
| 343 | guild_log_doc = await db.LOGGING.find_one( |
||
| 344 | {"_id": ctx.guild.id} |
||
| 345 | ) |
||
| 346 | if guild_log_doc["modlog"]: |
||
| 347 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 348 | |||
| 349 | await channel.send(embed=discord.Embed( |
||
| 350 | title="🧹 Kick", |
||
| 351 | description=f"{member.mention} has been kicked by {ctx.author.mention}", |
||
| 352 | color=var.C_GREEN |
||
| 353 | ) |
||
| 354 | ) |
||
| 355 | elif member == ctx.author: |
||
| 356 | await ctx.send("You can't kick yourself :eyes:") |
||
| 357 | |||
| 358 | else: |
||
| 359 | await ctx.send(embed=discord.Embed( |
||
| 360 | description="🚫 You need to define the member to kick them", |
||
| 361 | color=var.C_RED |
||
| 362 | ).add_field( |
||
| 363 | name="Format", |
||
| 364 | value=f"`{await get_prefix(ctx)}kick <member>`" |
||
| 365 | ).set_footer( |
||
| 366 | text="For user either Member mention or Member ID can be used") |
||
| 367 | ) |
||
| 368 | |||
| 369 | @kick.error |
||
| 370 | async def kick_error(self, ctx, error): |
||
| 371 | if isinstance(error, commands.CommandInvokeError): |
||
| 372 | await ctx.send( |
||
| 373 | embed=discord.Embed( |
||
| 374 | title="Permission error", |
||
| 375 | description=( |
||
| 376 | "🚫 I don't have permissions to kick the member," |
||
| 377 | " make sure that I have kick members permission" |
||
| 378 | " and my role is placed above the highest role " |
||
| 379 | "which the member has" |
||
| 380 | ), |
||
| 381 | color=var.C_RED |
||
| 382 | ) |
||
| 383 | ) |
||
| 384 | |||
| 385 | @commands.command(aliases=["nickname", "changenick"]) |
||
| 386 | @commands.has_permissions(change_nickname=True) |
||
| 387 | @has_command_permission() |
||
| 388 | async def nick(self, ctx, member: discord.Member = None, *, nick=None): |
||
| 389 | if member and nick is not None: |
||
| 390 | previous_nick = member.nick |
||
| 391 | await member.edit(nick=nick) |
||
| 392 | await ctx.send( |
||
| 393 | embed=discord.Embed( |
||
| 394 | description=( |
||
| 395 | f"{var.E_ACCEPT} Nickname changed " |
||
| 396 | f"for `{member}` to {nick}" |
||
| 397 | ), |
||
| 398 | color=var.C_GREEN |
||
| 399 | ) |
||
| 400 | ) |
||
| 401 | guild_log_doc = await db.LOGGING.find_one( |
||
| 402 | {"_id": ctx.guild.id} |
||
| 403 | ) |
||
| 404 | if guild_log_doc["modlog"]: |
||
| 405 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 406 | |||
| 407 | await channel.send(embed=discord.Embed( |
||
| 408 | title="🧹 Nickname change", |
||
| 409 | description=f"{member.mention}'s nickname has been changed by {ctx.author.mention} to {nick}", |
||
| 410 | color=var.C_GREEN |
||
| 411 | ).add_field( |
||
| 412 | name="Previous nick", |
||
| 413 | value=previous_nick |
||
| 414 | ) |
||
| 415 | ) |
||
| 416 | else: |
||
| 417 | await ctx.send( |
||
| 418 | embed=discord.Embed( |
||
| 419 | description=( |
||
| 420 | "🚫 You need to define both the member" |
||
| 421 | " and their new nick" |
||
| 422 | ), |
||
| 423 | color=var.C_RED |
||
| 424 | ).add_field( |
||
| 425 | name="Format", |
||
| 426 | value=f"`{await get_prefix(ctx)}nick <member> <new nick>`" |
||
| 427 | ).set_footer( |
||
| 428 | text="For Member either mention or Member ID can be used" |
||
| 429 | ) |
||
| 430 | ) |
||
| 431 | |||
| 432 | @nick.error |
||
| 433 | async def nick_error(self, ctx, error): |
||
| 434 | if isinstance(error, commands.CommandInvokeError): |
||
| 435 | await ctx.send( |
||
| 436 | embed=discord.Embed( |
||
| 437 | title="Permission error", |
||
| 438 | description=( |
||
| 439 | "🚫 I don't have permissions to change the nickname " |
||
| 440 | "of the member, make sure that I have change nickname " |
||
| 441 | "permission and my role is placed above the highest " |
||
| 442 | "role which the member has" |
||
| 443 | ), |
||
| 444 | color=var.C_RED |
||
| 445 | ) |
||
| 446 | ) |
||
| 447 | |||
| 448 | @commands.command(aliases=["clean", "clear"]) |
||
| 449 | @commands.has_permissions(manage_messages=True) |
||
| 450 | @has_command_permission() |
||
| 451 | async def purge(self, ctx, limit: int = None): |
||
| 452 | if limit is not None: |
||
| 453 | await ctx.channel.purge(limit=limit + 1) |
||
| 454 | |||
| 455 | info = await ctx.send(embed=discord.Embed( |
||
| 456 | description=f"Deleted {limit} messages", |
||
| 457 | color=var.C_ORANGE) |
||
| 458 | ) |
||
| 459 | await asyncio.sleep(1) |
||
| 460 | await info.delete() |
||
| 461 | |||
| 462 | guild_log_doc = await db.LOGGING.find_one( |
||
| 463 | {"_id": ctx.guild.id} |
||
| 464 | ) |
||
| 465 | if guild_log_doc["modlog"]: |
||
| 466 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 467 | |||
| 468 | await channel.send(embed=discord.Embed( |
||
| 469 | title="🧹 Purge", |
||
| 470 | description=f"{ctx.author.mention} has deleted {limit} messages from {ctx.channel.mention}", |
||
| 471 | color=var.C_GREEN |
||
| 472 | ) |
||
| 473 | ) |
||
| 474 | else: |
||
| 475 | await ctx.send( |
||
| 476 | embed=discord.Embed( |
||
| 477 | description=( |
||
| 478 | "🚫 You need to define the amount" |
||
| 479 | " to delete messages too! Make sure the amount is numerical." |
||
| 480 | ), |
||
| 481 | color=var.C_RED |
||
| 482 | ).add_field( |
||
| 483 | name="Format", |
||
| 484 | value=f"`{await get_prefix(ctx)}purge <amount>`" |
||
| 485 | ) |
||
| 486 | ) |
||
| 487 | |||
| 488 | @purge.error |
||
| 489 | async def purge_error(self, ctx, error): |
||
| 490 | if isinstance(error, commands.CommandInvokeError): |
||
| 491 | await ctx.send( |
||
| 492 | embed=discord.Embed( |
||
| 493 | title="Permission error", |
||
| 494 | description="🚫 I don't have permissions to delete messages", |
||
| 495 | color=var.C_RED |
||
| 496 | ) |
||
| 497 | ) |
||
| 498 | |||
| 499 | View Code Duplication | @commands.command(aliases=["giverole"]) |
|
| 500 | @has_command_permission() |
||
| 501 | async def addrole( |
||
| 502 | self, ctx, member: discord.Member = None, role: discord.Role = None |
||
| 503 | ): |
||
| 504 | if member and role is not None: |
||
| 505 | try: |
||
| 506 | await member.add_roles(role) |
||
| 507 | await ctx.send( |
||
| 508 | embed=discord.Embed( |
||
| 509 | description=( |
||
| 510 | f"Successfully updated {member.mention} " |
||
| 511 | f"with {role.mention} role" |
||
| 512 | ), |
||
| 513 | color=var.C_GREEN |
||
| 514 | ) |
||
| 515 | ) |
||
| 516 | |||
| 517 | except discord.Forbidden: |
||
| 518 | await ctx.send( |
||
| 519 | embed=discord.Embed( |
||
| 520 | title="Missing permissions", |
||
| 521 | description=( |
||
| 522 | f"I don't have permissions to update the roles" |
||
| 523 | f" of {member.mention}, either I don't have the" |
||
| 524 | f" permission or the member is above me" |
||
| 525 | ), |
||
| 526 | color=var.C_RED |
||
| 527 | ) |
||
| 528 | ) |
||
| 529 | else: |
||
| 530 | await ctx.send( |
||
| 531 | embed=discord.Embed( |
||
| 532 | title=f"🚫 Missing arguments", |
||
| 533 | description="You need to define both member and role", |
||
| 534 | color=var.C_RED |
||
| 535 | ).add_field( |
||
| 536 | name="Format", |
||
| 537 | value=f"```{await get_prefix(ctx)}addrole <member> <role>```" |
||
| 538 | ).set_footer( |
||
| 539 | text=( |
||
| 540 | "For both member and role, " |
||
| 541 | "either ping or ID can be used" |
||
| 542 | ) |
||
| 543 | ) |
||
| 544 | ) |
||
| 545 | |||
| 546 | View Code Duplication | @commands.command(name="removerole") |
|
| 547 | @has_command_permission() |
||
| 548 | async def remove_role( |
||
| 549 | self, ctx, member: discord.Member = None, role: discord.Role = None |
||
| 550 | ): |
||
| 551 | if member and role is not None: |
||
| 552 | try: |
||
| 553 | await member.remove_roles(role) |
||
| 554 | await ctx.send( |
||
| 555 | embed=discord.Embed( |
||
| 556 | description=( |
||
| 557 | f"Successfully updated {member.mention} " |
||
| 558 | f"by removing {role.mention} role" |
||
| 559 | ), |
||
| 560 | color=var.C_GREEN |
||
| 561 | ) |
||
| 562 | ) |
||
| 563 | |||
| 564 | except discord.Forbidden: |
||
| 565 | await ctx.send( |
||
| 566 | embed=discord.Embed( |
||
| 567 | title="Missing permissions", |
||
| 568 | description=( |
||
| 569 | f"I don't have permissions to update the roles of" |
||
| 570 | f" {member.mention}, either I don't have the" |
||
| 571 | f" permission or the member is above me" |
||
| 572 | ), |
||
| 573 | color=var.C_RED |
||
| 574 | ) |
||
| 575 | ) |
||
| 576 | else: |
||
| 577 | await ctx.send( |
||
| 578 | embed=discord.Embed( |
||
| 579 | title=f"🚫 Missing arguments", |
||
| 580 | description="You need to define both member and role", |
||
| 581 | color=var.C_RED |
||
| 582 | ).add_field( |
||
| 583 | name="Format", |
||
| 584 | value=( |
||
| 585 | f"```{await get_prefix(ctx)}removerole" |
||
| 586 | f" <member> <role>```" |
||
| 587 | ) |
||
| 588 | ).set_footer( |
||
| 589 | text=( |
||
| 590 | "For both member and role," |
||
| 591 | " either ping or ID can be used" |
||
| 592 | ) |
||
| 593 | ) |
||
| 594 | ) |
||
| 595 | |||
| 596 | View Code Duplication | @commands.command(name="massrole") |
|
| 597 | @has_command_permission() |
||
| 598 | async def mass_role( |
||
| 599 | self, ctx, role: discord.Role = None, role2: discord.Role = None |
||
| 600 | ): |
||
| 601 | if role is not None and role2 is not None: |
||
| 602 | bot_msg = await ctx.send( |
||
| 603 | embed=discord.Embed( |
||
| 604 | title="Confirmation", |
||
| 605 | description=( |
||
| 606 | f"Are you sure you want to update all members" |
||
| 607 | f" with the role {role.mention} with {role2.mention}?" |
||
| 608 | ), |
||
| 609 | color=var.C_BLUE |
||
| 610 | ).add_field( |
||
| 611 | name=( |
||
| 612 | "Note that this action is irreversible " |
||
| 613 | "and cannot be stopped once started" |
||
| 614 | ), |
||
| 615 | value=( |
||
| 616 | f"{var.E_ACCEPT} to accept\n" |
||
| 617 | f"{var.E_ENABLE} to accept with live stats\n" |
||
| 618 | f"{var.E_DECLINE} to decline" |
||
| 619 | ) |
||
| 620 | ) |
||
| 621 | ) |
||
| 622 | |||
| 623 | await bot_msg.add_reaction(var.E_ACCEPT) |
||
| 624 | await bot_msg.add_reaction(var.E_ENABLE) |
||
| 625 | await bot_msg.add_reaction(var.E_DECLINE) |
||
| 626 | |||
| 627 | def reaction_check(r, u): |
||
| 628 | return u == ctx.author and r.message == bot_msg |
||
| 629 | |||
| 630 | reaction, _ = await self.bot.wait_for( |
||
| 631 | "reaction_add", check=reaction_check |
||
| 632 | ) |
||
| 633 | |||
| 634 | updates = False |
||
| 635 | |||
| 636 | try: |
||
| 637 | await bot_msg.clear_reactions() |
||
| 638 | except Exception: |
||
| 639 | pass |
||
| 640 | |||
| 641 | if str(reaction.emoji) == var.E_DECLINE: |
||
| 642 | return await ctx.send("Cancelled mass role update") |
||
| 643 | |||
| 644 | if str(reaction.emoji) == var.E_ENABLE: |
||
| 645 | updates = True |
||
| 646 | |||
| 647 | if ( |
||
| 648 | str(reaction.emoji) == var.E_ENABLE |
||
| 649 | or str(reaction.emoji) == var.E_ACCEPT |
||
| 650 | ): |
||
| 651 | |||
| 652 | count = 0 |
||
| 653 | |||
| 654 | for member in [ |
||
| 655 | member |
||
| 656 | for member in ctx.guild.members |
||
| 657 | if role in member.roles |
||
| 658 | ]: |
||
| 659 | try: |
||
| 660 | await member.add_roles(role2) |
||
| 661 | count += 1 |
||
| 662 | |||
| 663 | if updates: |
||
| 664 | await ctx.send(f"{member} updated") |
||
| 665 | |||
| 666 | except discord.Forbidden: |
||
| 667 | await ctx.send( |
||
| 668 | embed=discord.Embed( |
||
| 669 | description=( |
||
| 670 | f"Error giving role to {member.mention}" |
||
| 671 | ), |
||
| 672 | color=var.C_ORANGE |
||
| 673 | ) |
||
| 674 | ) |
||
| 675 | |||
| 676 | await asyncio.sleep(1) |
||
| 677 | |||
| 678 | await ctx.send( |
||
| 679 | f"Done, updated **{count}** members " |
||
| 680 | f"with the {role2.name} role" |
||
| 681 | ) |
||
| 682 | |||
| 683 | else: |
||
| 684 | await ctx.send( |
||
| 685 | embed=discord.Embed( |
||
| 686 | title=f"🚫 Missing arguments", |
||
| 687 | description=( |
||
| 688 | "You need to define both Role 1 and Role 2\n`role1` " |
||
| 689 | "are the members having that role and `role2` is the" |
||
| 690 | " one to be given to them" |
||
| 691 | ), |
||
| 692 | color=var.C_RED |
||
| 693 | ).add_field( |
||
| 694 | name="Format", |
||
| 695 | value=f"```{await get_prefix(ctx)}massrole <role1> <role2>```" |
||
| 696 | ).set_footer( |
||
| 697 | text="For role, either ping or ID can be used" |
||
| 698 | ) |
||
| 699 | ) |
||
| 700 | |||
| 701 | View Code Duplication | @commands.command(name="massroleremove") |
|
| 702 | @has_command_permission() |
||
| 703 | async def mass_role_remove( |
||
| 704 | self, ctx, role: discord.Role = None, role2: discord.Role = None |
||
| 705 | ): |
||
| 706 | if role is not None and role2 is not None: |
||
| 707 | bot_msg = await ctx.send( |
||
| 708 | embed=discord.Embed( |
||
| 709 | title="Confirmation", |
||
| 710 | description=( |
||
| 711 | f"Are you sure you want to update" |
||
| 712 | f" all members with the role {role.mention}" |
||
| 713 | f" by removing {role2.mention}?" |
||
| 714 | ), |
||
| 715 | color=var.C_BLUE |
||
| 716 | ).add_field( |
||
| 717 | name=( |
||
| 718 | "Note that this action is irreversable" |
||
| 719 | " and cannot be stopped once started" |
||
| 720 | ), |
||
| 721 | value=( |
||
| 722 | f"{var.E_ACCEPT} to accept\n{var.E_ENABLE}" |
||
| 723 | f" to accept with live stats\n{var.E_DECLINE} to decline" |
||
| 724 | ) |
||
| 725 | ) |
||
| 726 | ) |
||
| 727 | |||
| 728 | await bot_msg.add_reaction(var.E_ACCEPT) |
||
| 729 | await bot_msg.add_reaction(var.E_ENABLE) |
||
| 730 | await bot_msg.add_reaction(var.E_DECLINE) |
||
| 731 | |||
| 732 | def reaction_check(r, u): |
||
| 733 | return u == ctx.author and r.message == bot_msg |
||
| 734 | |||
| 735 | reaction, _ = await self.bot.wait_for( |
||
| 736 | "reaction_add", check=reaction_check |
||
| 737 | ) |
||
| 738 | |||
| 739 | updates = False |
||
| 740 | |||
| 741 | try: |
||
| 742 | await bot_msg.clear_reactions() |
||
| 743 | |||
| 744 | except Exception: |
||
| 745 | pass |
||
| 746 | |||
| 747 | if str(reaction.emoji) == var.E_DECLINE: |
||
| 748 | return await ctx.send("Cancelled mass role update") |
||
| 749 | |||
| 750 | if str(reaction.emoji) == var.E_ENABLE: |
||
| 751 | updates = True |
||
| 752 | |||
| 753 | if str(reaction.emoji) == var.E_ENABLE or str( |
||
| 754 | reaction.emoji) == var.E_ACCEPT: |
||
| 755 | count = 0 |
||
| 756 | for member in [ |
||
| 757 | member |
||
| 758 | for member in ctx.guild.members |
||
| 759 | if role in member.roles |
||
| 760 | ]: |
||
| 761 | try: |
||
| 762 | await member.remove_roles(role2) |
||
| 763 | count += 1 |
||
| 764 | if updates: |
||
| 765 | await ctx.send(f"{member} updated") |
||
| 766 | |||
| 767 | except discord.Forbidden: |
||
| 768 | await ctx.send( |
||
| 769 | embed=discord.Embed( |
||
| 770 | description=( |
||
| 771 | f"Error giving role to {member.mention}" |
||
| 772 | ), |
||
| 773 | color=var.C_ORANGE |
||
| 774 | ) |
||
| 775 | ) |
||
| 776 | await asyncio.sleep(1) |
||
| 777 | |||
| 778 | await ctx.send( |
||
| 779 | f"Done," |
||
| 780 | f" updated **{count}** members with the {role2.name} role" |
||
| 781 | ) |
||
| 782 | else: |
||
| 783 | |||
| 784 | await ctx.send( |
||
| 785 | embed=discord.Embed( |
||
| 786 | title=f"🚫 Missing arguments", |
||
| 787 | description=( |
||
| 788 | "You need to define both Role 1 and Role 2\n" |
||
| 789 | "`role1` are the members having that role" |
||
| 790 | " and `role2` is the one to be removed from them" |
||
| 791 | ), |
||
| 792 | color=var.C_RED |
||
| 793 | ).add_field( |
||
| 794 | name="Format", |
||
| 795 | value=( |
||
| 796 | f"```{await get_prefix(ctx)}massroleremove" |
||
| 797 | f" <role1> <role2>```" |
||
| 798 | ) |
||
| 799 | ).set_footer( |
||
| 800 | text="For role, either ping or ID can be used" |
||
| 801 | ) |
||
| 802 | ) |
||
| 803 | |||
| 804 | @commands.command() |
||
| 805 | @has_command_permission() |
||
| 806 | async def warn(self, ctx, member: discord.Member = None, *, reason=None): |
||
| 807 | if member and reason is not None: |
||
| 808 | guild_col = db.WARNINGS_DATABASE[str(ctx.guild.id)] |
||
| 809 | user_warns = await guild_col.find_one({"_id": member.id}) |
||
| 810 | |||
| 811 | if user_warns is None: |
||
| 812 | new_warns = [reason] |
||
| 813 | await guild_col.insert_one( |
||
| 814 | {"_id": member.id, "warns": new_warns} |
||
| 815 | ) |
||
| 816 | |||
| 817 | else: |
||
| 818 | current_warns = user_warns["warns"] |
||
| 819 | new_warns = current_warns.copy() |
||
| 820 | new_warns.append(reason) |
||
| 821 | new_data = { |
||
| 822 | "$set": { |
||
| 823 | "warns": new_warns |
||
| 824 | } |
||
| 825 | } |
||
| 826 | |||
| 827 | await guild_col.update_one(user_warns, new_data) |
||
| 828 | |||
| 829 | await ctx.send( |
||
| 830 | content=f"{member.mention} has been warned!", |
||
| 831 | embed=discord.Embed( |
||
| 832 | description=( |
||
| 833 | f"Reason: **{reason}**\n" |
||
| 834 | f"Total warns: **{len(new_warns)}**" |
||
| 835 | ), |
||
| 836 | color=var.C_BLUE |
||
| 837 | ).set_footer( |
||
| 838 | text=f"Moderator: {ctx.author}" |
||
| 839 | ) |
||
| 840 | ) |
||
| 841 | guild_log_doc = await db.LOGGING.find_one( |
||
| 842 | {"_id": ctx.guild.id} |
||
| 843 | ) |
||
| 844 | if guild_log_doc["modlog"]: |
||
| 845 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 846 | |||
| 847 | await channel.send(embed=discord.Embed( |
||
| 848 | title="⚠️ Warn", |
||
| 849 | description=f"{member.mention} has been warned by {ctx.author.mention}", |
||
| 850 | color=var.C_GREEN |
||
| 851 | ).add_field( |
||
| 852 | name="Total warns now", |
||
| 853 | value=len(new_warns) |
||
| 854 | ) |
||
| 855 | ) |
||
| 856 | |||
| 857 | elif member is not None and reason is None: |
||
| 858 | await ctx.send("Reason is required too!") |
||
| 859 | |||
| 860 | else: |
||
| 861 | await ctx.send( |
||
| 862 | embed=discord.Embed( |
||
| 863 | title=f"🚫 Missing arguments", |
||
| 864 | description=( |
||
| 865 | "You need to define both the member" |
||
| 866 | " and reason to warn them!" |
||
| 867 | ), |
||
| 868 | color=var.C_RED |
||
| 869 | ).add_field( |
||
| 870 | name="Format", |
||
| 871 | value=f"```{await get_prefix(ctx)}warn <member> <reason>```" |
||
| 872 | ) |
||
| 873 | ) |
||
| 874 | |||
| 875 | @commands.command(name="removewarn", aliases=["remove-warn", "unwarn"]) |
||
| 876 | @has_command_permission() |
||
| 877 | async def remove_warn( |
||
| 878 | self, ctx, member: discord.Member = None, position=None |
||
| 879 | ): |
||
| 880 | if member and position is not None: |
||
| 881 | try: |
||
| 882 | position = int(position) |
||
| 883 | |||
| 884 | except ValueError: |
||
| 885 | await ctx.send( |
||
| 886 | embed=discord.Embed( |
||
| 887 | description=f"The position should be a number!", |
||
| 888 | color=var.C_RED) |
||
| 889 | ) |
||
| 890 | return |
||
| 891 | |||
| 892 | guild_col = db.WARNINGS_DATABASE[str(ctx.guild.id)] |
||
| 893 | user_doc = await guild_col.find_one({"_id": member.id}) |
||
| 894 | |||
| 895 | if user_doc is None: |
||
| 896 | await ctx.send( |
||
| 897 | embed=discord.Embed( |
||
| 898 | description=f"{member.mention} does not have any warns", |
||
| 899 | color=var.C_RED |
||
| 900 | ).set_footer( |
||
| 901 | text=( |
||
| 902 | "Note that this warn's position has been taken by" |
||
| 903 | " the warn below it, therefore moving all warns " |
||
| 904 | "below this one position above" |
||
| 905 | ) |
||
| 906 | ) |
||
| 907 | ) |
||
| 908 | |||
| 909 | else: |
||
| 910 | warns = user_doc["warns"] |
||
| 911 | if len(warns) - 1 >= position - 1: |
||
| 912 | reason = warns[position - 1] |
||
| 913 | new_warns = warns.copy() |
||
| 914 | removed_warn = new_warns.pop(position - 1) |
||
| 915 | new_data = { |
||
| 916 | "$set": { |
||
| 917 | "warns": new_warns |
||
| 918 | } |
||
| 919 | } |
||
| 920 | |||
| 921 | await guild_col.update_one(user_doc, new_data) |
||
| 922 | await ctx.send( |
||
| 923 | embed=discord.Embed( |
||
| 924 | description=( |
||
| 925 | f"{var.E_ACCEPT} Removed {position} warn with " |
||
| 926 | f"the reason **{reason}** from {member.mention}" |
||
| 927 | ), |
||
| 928 | color=var.C_GREEN |
||
| 929 | ).set_footer( |
||
| 930 | text=( |
||
| 931 | "Note that if there are any warns below this " |
||
| 932 | "one then they are moved one position up to " |
||
| 933 | "take the removed warn's place" |
||
| 934 | ) |
||
| 935 | ) |
||
| 936 | ) |
||
| 937 | guild_log_doc = await db.LOGGING.find_one( |
||
| 938 | {"_id": ctx.guild.id} |
||
| 939 | ) |
||
| 940 | if guild_log_doc["modlog"]: |
||
| 941 | channel = self.bot.get_channel(guild_log_doc["channel_id"]) |
||
| 942 | |||
| 943 | await channel.send(embed=discord.Embed( |
||
| 944 | title="⚠️ Remove warn", |
||
| 945 | description=f"{member.mention} has been unwarned by {ctx.author.mention}", |
||
| 946 | color=var.C_BLUE |
||
| 947 | ).add_field( |
||
| 948 | name="Removed warn", |
||
| 949 | value=removed_warn |
||
| 950 | ) |
||
| 951 | ) |
||
| 952 | else: |
||
| 953 | await ctx.send( |
||
| 954 | embed=discord.Embed( |
||
| 955 | description=( |
||
| 956 | f"{member.mention} does not have" |
||
| 957 | f" {position} warn(s)" |
||
| 958 | ), |
||
| 959 | color=var.C_RED |
||
| 960 | ) |
||
| 961 | ) |
||
| 962 | |||
| 963 | else: |
||
| 964 | await ctx.send( |
||
| 965 | embed=discord.Embed( |
||
| 966 | title=f"🚫 Missing arguments", |
||
| 967 | description=( |
||
| 968 | "You need to define both the member " |
||
| 969 | "and the warn position to remove the warn" |
||
| 970 | ), |
||
| 971 | color=var.C_RED |
||
| 972 | ).add_field( |
||
| 973 | name="Format", |
||
| 974 | value=( |
||
| 975 | f"```{await get_prefix(ctx)}removewarn " |
||
| 976 | "<member> <position>```" |
||
| 977 | ) |
||
| 978 | ).set_footer( |
||
| 979 | text="Note that position here is just a number" |
||
| 980 | ) |
||
| 981 | ) |
||
| 982 | |||
| 983 | @commands.command() |
||
| 984 | @has_command_permission() |
||
| 985 | async def warns(self, ctx, member: discord.Member = None): |
||
| 986 | if member is not None: |
||
| 987 | guild_col = db.WARNINGS_DATABASE[str(ctx.guild.id)] |
||
| 988 | userdata = await guild_col.find_one({"_id": member.id}) |
||
| 989 | |||
| 990 | if userdata is None: |
||
| 991 | await ctx.send(f"{member} does not have any warnings") |
||
| 992 | |||
| 993 | else: |
||
| 994 | warns = userdata["warns"] |
||
| 995 | embed = discord.Embed( |
||
| 996 | title=f"{member} warns", color=var.C_MAIN |
||
| 997 | ) |
||
| 998 | for i in warns: |
||
| 999 | embed.add_field( |
||
| 1000 | name=f"Warn {warns.index(i) + 1}", value=i, |
||
| 1001 | inline=False |
||
| 1002 | ) |
||
| 1003 | await ctx.send(embed=embed) |
||
| 1004 | guild_doc = await db.LOGGING.find_one( |
||
| 1005 | {"_id": ctx.guild.id} |
||
| 1006 | ) |
||
| 1007 | if guild_doc["modlog"]: |
||
| 1008 | channel = self.bot.get_channel(guild_doc["channel_id"]) |
||
| 1009 | |||
| 1010 | await channel.send(embed=discord.Embed( |
||
| 1011 | title="New warn", |
||
| 1012 | description=f"{member.mention} has been warned by {ctx.author.mention}", |
||
| 1013 | color=var.C_GREEN |
||
| 1014 | )) |
||
| 1015 | |||
| 1016 | else: |
||
| 1017 | await ctx.send( |
||
| 1018 | embed=discord.Embed( |
||
| 1019 | title=f"🚫 Missing arguments", |
||
| 1020 | description=( |
||
| 1021 | "You need to define the member to view their warns" |
||
| 1022 | ), |
||
| 1023 | color=var.C_RED |
||
| 1024 | ).add_field( |
||
| 1025 | name="Format", |
||
| 1026 | value=f"```{await get_prefix(ctx)}warns <member>```" |
||
| 1027 | ) |
||
| 1028 | ) |
||
| 1029 | |||
| 1030 | @commands.command() |
||
| 1031 | async def modlog(self, ctx): |
||
| 1032 | guild_doc = await db.LOGGING.find_one({ |
||
| 1033 | "_id": ctx.guild.id |
||
| 1034 | }) |
||
| 1035 | |||
| 1036 | status = False if not guild_doc or not guild_doc["modlog"] else True |
||
| 1037 | |||
| 1038 | def check(reaction, user): |
||
| 1039 | return user == ctx.author and reaction.message == bot_msg |
||
| 1040 | |||
| 1041 | def message_check(message): |
||
| 1042 | return ( |
||
| 1043 | message.author == ctx.author |
||
| 1044 | and message.channel.id == ctx.channel.id |
||
| 1045 | ) |
||
| 1046 | |||
| 1047 | embed = discord.Embed(title="Mod log") |
||
| 1048 | if status: |
||
| 1049 | embed.description = ( |
||
| 1050 | "Moderation log is currently enabled" |
||
| 1051 | f"\nReact to the {var.E_DISABLE} emoji to disable it." |
||
| 1052 | ) |
||
| 1053 | embed.color = var.C_GREEN |
||
| 1054 | |||
| 1055 | else: |
||
| 1056 | embed.description = ( |
||
| 1057 | "Moderation log is currently disabled" |
||
| 1058 | f"\nReact to the {var.E_ENABLE} emoji to disable it." |
||
| 1059 | ) |
||
| 1060 | embed.color = var.C_RED |
||
| 1061 | |||
| 1062 | bot_msg = await ctx.send(embed=embed) |
||
| 1063 | |||
| 1064 | await bot_msg.add_reaction( |
||
| 1065 | var.E_DISABLE |
||
| 1066 | if status else |
||
| 1067 | var.E_ENABLE |
||
| 1068 | ) |
||
| 1069 | reaction, _ = await self.bot.wait_for( |
||
| 1070 | "reaction_add", |
||
| 1071 | check=check, |
||
| 1072 | timeout=60 |
||
| 1073 | ) |
||
| 1074 | |||
| 1075 | if str(reaction.emoji) == var.E_ENABLE: |
||
| 1076 | if not guild_doc: |
||
| 1077 | await ctx.send("Send the channel where you would like to log moderation events!") |
||
| 1078 | while True: |
||
| 1079 | user_msg = await self.bot.wait_for("message", |
||
| 1080 | check=message_check, |
||
| 1081 | timeout=60 |
||
| 1082 | ) |
||
| 1083 | try: |
||
| 1084 | channel = self.bot.get_channel( |
||
| 1085 | int(user_msg.content.strip("<>#")) |
||
| 1086 | ) |
||
| 1087 | break |
||
| 1088 | except Exception: |
||
| 1089 | await ctx.send("Invalid channel ID, try again.") |
||
| 1090 | |||
| 1091 | |||
| 1092 | await db.LOGGING.insert_one( |
||
| 1093 | { |
||
| 1094 | "_id": ctx.guild.id, |
||
| 1095 | "channel_id": channel.id, |
||
| 1096 | "modlog": True |
||
| 1097 | } |
||
| 1098 | ) |
||
| 1099 | else: |
||
| 1100 | await db.LOGGING.insert( |
||
| 1101 | guild_doc, |
||
| 1102 | {"set":{'modlog': True}} |
||
| 1103 | ) |
||
| 1104 | |||
| 1105 | elif str(reaction.emoji) == var.E_DISABLE: |
||
| 1106 | await db.LOGGING.update_db( |
||
| 1107 | guild_doc, |
||
| 1108 | {"set": {'modlog': True}} |
||
| 1109 | ) |
||
| 1110 | |||
| 1111 | await ctx.send(embed=discord.Embed( |
||
| 1112 | description=f"Successfully {'disabled' if status else 'enabled'} moderation logging.", |
||
| 1113 | color=var.C_RED if status else var.C_GREEN |
||
| 1114 | )) |
||
| 1115 | |||
| 1116 | def setup(bot): |
||
| 1117 | bot.add_cog(Moderation(bot)) |
||
| 1118 |