feat(moderation): added discord channel logging for moderations
Some checks failed
Pylint / Pylint (3.10) (push) Failing after 59s

This commit is contained in:
Seaswimmer 2023-10-23 20:09:17 -04:00
parent f2e9657eff
commit 7ee1554080
No known key found for this signature in database
GPG key ID: 5019678FD9CF50D8

View file

@ -26,7 +26,8 @@ class Moderation(commands.Cog):
) )
self.config.register_guild( self.config.register_guild(
ignore_other_bots = True, ignore_other_bots = True,
dm_users = True dm_users = True,
log_channel = " "
) )
disable_dateutil() disable_dateutil()
self.handle_expiry.start() # pylint: disable=no-member self.handle_expiry.start() # pylint: disable=no-member
@ -208,6 +209,7 @@ class Moderation(commands.Cog):
database.commit() database.commit()
database.close() database.close()
self.logger.debug("MySQL row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, 0, NULL, NULL, 0", guild_id, moderation_id, timestamp, moderation_type, target_id, author_id, duration, end_timestamp, reason) self.logger.debug("MySQL row inserted into moderation_%s!\n%s, %s, %s, %s, %s, %s, %s, %s, 0, NULL, NULL, 0", guild_id, moderation_id, timestamp, moderation_type, target_id, author_id, duration, end_timestamp, reason)
return moderation_id
async def get_next_case_number(self, guild_id: str, cursor = None): async def get_next_case_number(self, guild_id: str, cursor = None):
"""This method returns the next case number from the MySQL table for a specific guild.""" """This method returns the next case number from the MySQL table for a specific guild."""
@ -251,12 +253,12 @@ class Moderation(commands.Cog):
} }
return user_dict return user_dict
async def embed_factory(self, embed_type: str, /, interaction: discord.Interaction = None, case_dict: dict = None, guild: discord.Guild = None, reason: str = None, moderation_type: str = None, response: discord.InteractionMessage = None, duration: timedelta = None): async def embed_factory(self, embed_type: str, /, interaction: discord.Interaction = None, case_dict: dict = None, guild: discord.Guild = None, reason: str = None, moderation_type: str = None, response: discord.InteractionMessage = None, duration: timedelta = None, resolved: bool = False):
"""This method creates an embed from set parameters, meant for either moderation logging or contacting the moderated user. """This method creates an embed from set parameters, meant for either moderation logging or contacting the moderated user.
Valid arguments for 'embed_type': Valid arguments for 'embed_type':
- 'message' - 'message'
- 'list' - WIP - 'log' - WIP
- 'case' - 'case'
Required arguments for 'message': Required arguments for 'message':
@ -266,6 +268,11 @@ class Moderation(commands.Cog):
- response - response
- duration (optional) - duration (optional)
Required arguments for 'log':
- interaction
- case_dict
- resolved (optional)
Required arguments for 'case': Required arguments for 'case':
- interaction - interaction
- case_dict""" - case_dict"""
@ -306,8 +313,62 @@ class Moderation(commands.Cog):
embed.add_field(name='Resolve Reason', value=f"Resolved by {resolved_name} ({resolved_user['id']}) for:\n```{case_dict['resolve_reason']}```", inline=False) embed.add_field(name='Resolve Reason', value=f"Resolved by {resolved_name} ({resolved_user['id']}) for:\n```{case_dict['resolve_reason']}```", inline=False)
return embed return embed
if embed_type == 'log':
if resolved:
target_user = await self.fetch_user_dict(interaction, case_dict['target_id'])
moderator_user = await self.fetch_user_dict(interaction, case_dict['moderator_id'])
target_name = f"`{target_user['name']}`" if target_user['discriminator'] == "0" else f"`{target_user['name']}#{target_user['discriminator']}`"
moderator_name = moderator_user['name'] if moderator_user['discriminator'] == "0" else f"{moderator_user['name']}#{moderator_user['discriminator']}"
embed = discord.Embed(title=f"📕 Case #{case_dict['moderation_id']} Resolved", color=await self.bot.get_embed_color(None))
embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Timestamp:** <t:{case_dict['timestamp']}> | <t:{case_dict['timestamp']}:R>"
if case_dict['duration'] != 'NULL':
td = timedelta(**{unit: int(val) for unit, val in zip(["hours", "minutes", "seconds"], case_dict["duration"].split(":"))})
duration_embed = f"{humanize.precisedelta(td)} | <t:{case_dict['end_timestamp']}:R>" if case_dict["expired"] == '0' else str(humanize.precisedelta(td))
embed.description = embed.description + f"\n**Duration:** {duration_embed}\n**Expired:** {bool(case_dict['expired'])}"
embed.add_field(name='Reason', value=f"```{case_dict['reason']}```", inline=False)
resolved_user = await self.fetch_user_dict(interaction, case_dict['resolved_by'])
resolved_name = resolved_user['name'] if resolved_user['discriminator'] == "0" else f"{resolved_user['name']}#{resolved_user['discriminator']}"
embed.add_field(name='Resolve Reason', value=f"Resolved by {resolved_name} ({resolved_user['id']}) for:\n```{case_dict['resolve_reason']}```", inline=False)
return embed
else:
target_user = await self.fetch_user_dict(interaction, case_dict['target_id'])
moderator_user = await self.fetch_user_dict(interaction, case_dict['moderator_id'])
target_name = f"`{target_user['name']}`" if target_user['discriminator'] == "0" else f"`{target_user['name']}#{target_user['discriminator']}`"
moderator_name = moderator_user['name'] if moderator_user['discriminator'] == "0" else f"{moderator_user['name']}#{moderator_user['discriminator']}"
embed = discord.Embed(title=f"📕 Case #{case_dict['moderation_id']}", color=await self.bot.get_embed_color(None))
embed.description = f"**Type:** {str.title(case_dict['moderation_type'])}\n**Target:** {target_name} ({target_user['id']})\n**Moderator:** {moderator_name} ({moderator_user['id']})\n**Timestamp:** <t:{case_dict['timestamp']}> | <t:{case_dict['timestamp']}:R>"
if case_dict['duration'] != 'NULL':
td = timedelta(**{unit: int(val) for unit, val in zip(["hours", "minutes", "seconds"], case_dict["duration"].split(":"))})
embed.description = embed.description + f"\n**Duration:** {humanize.precisedelta(td)} | <t:{case_dict['end_timestamp']}:R>"
embed.add_field(name='Reason', value=f"```{case_dict['reason']}```", inline=False)
return embed
raise(TypeError("'type' argument is invalid!")) raise(TypeError("'type' argument is invalid!"))
async def fetch_case(self, moderation_id: int, guild_id: str):
"""This method fetches a case from the database and returns the case's dictionary."""
database = await self.connect()
cursor = database.cursor()
query = "SELECT * FROM moderation_%s WHERE moderation_id = %s;"
cursor.execute(query, (guild_id, moderation_id))
result = cursor.fetchone()
cursor.close()
database.close()
return self.generate_dict(result)
async def log(self, interaction: discord.Interaction, moderation_id: int, resolved: bool = False):
"""This method sends a message to the guild's configured logging channel when an infraction takes place."""
logging_channel_id = await self.config.guild(interaction.guild.id).logging_channel()
if logging_channel_id != " ":
logging_channel = interaction.guild.get_channel(logging_channel_id)
case = await self.fetch_case(moderation_id, interaction.guild.id)
if case:
embed = await self.embed_factory('log', interaction=interaction, case_dict=case, resolved=resolved)
try:
await logging_channel.send(embed=embed)
except discord.errors.Forbidden:
return
@app_commands.command(name="note") @app_commands.command(name="note")
async def note(self, interaction: discord.Interaction, target: discord.User, reason: str, silent: bool = None): async def note(self, interaction: discord.Interaction, target: discord.User, reason: str, silent: bool = None):
"""Add a note to a user. """Add a note to a user.
@ -337,7 +398,8 @@ class Moderation(commands.Cog):
await target.send(embed=embed) await target.send(embed=embed)
except discord.errors.HTTPException: except discord.errors.HTTPException:
pass pass
await self.mysql_log(interaction.guild.id, interaction.user.id, 'NOTE', target.id, 'NULL', reason) moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'NOTE', target.id, 'NULL', reason)
await self.log(interaction, moderation_id)
@app_commands.command(name="warn") @app_commands.command(name="warn")
async def warn(self, interaction: discord.Interaction, target: discord.Member, reason: str, silent: bool = None): async def warn(self, interaction: discord.Interaction, target: discord.Member, reason: str, silent: bool = None):
@ -368,7 +430,8 @@ class Moderation(commands.Cog):
await target.send(embed=embed) await target.send(embed=embed)
except discord.errors.HTTPException: except discord.errors.HTTPException:
pass pass
await self.mysql_log(interaction.guild.id, interaction.user.id, 'WARN', target.id, 'NULL', reason) moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'WARN', target.id, 'NULL', reason)
await self.log(interaction, moderation_id)
@app_commands.command(name="mute") @app_commands.command(name="mute")
async def mute(self, interaction: discord.Interaction, target: discord.Member, duration: str, reason: str, silent: bool = None): async def mute(self, interaction: discord.Interaction, target: discord.Member, duration: str, reason: str, silent: bool = None):
@ -417,7 +480,8 @@ class Moderation(commands.Cog):
await target.send(embed=embed) await target.send(embed=embed)
except discord.errors.HTTPException: except discord.errors.HTTPException:
pass pass
await self.mysql_log(interaction.guild.id, interaction.user.id, 'MUTE', target.id, parsed_time, reason) moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'MUTE', target.id, parsed_time, reason)
await self.log(interaction, moderation_id)
@app_commands.command(name="unmute") @app_commands.command(name="unmute")
async def unmute(self, interaction: discord.Interaction, target: discord.Member, reason: str = None, silent: bool = None): async def unmute(self, interaction: discord.Interaction, target: discord.Member, reason: str = None, silent: bool = None):
@ -460,7 +524,8 @@ class Moderation(commands.Cog):
await target.send(embed=embed) await target.send(embed=embed)
except discord.errors.HTTPException: except discord.errors.HTTPException:
pass pass
await self.mysql_log(interaction.guild.id, interaction.user.id, 'UNMUTE', target.id, 'NULL', reason) moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'UNMUTE', target.id, 'NULL', reason)
await self.log(interaction, moderation_id)
@app_commands.command(name="kick") @app_commands.command(name="kick")
async def kick(self, interaction: discord.Interaction, target: discord.Member, reason: str, silent: bool = None): async def kick(self, interaction: discord.Interaction, target: discord.Member, reason: str, silent: bool = None):
@ -496,7 +561,8 @@ class Moderation(commands.Cog):
except discord.errors.HTTPException: except discord.errors.HTTPException:
pass pass
await target.kick(f"Kicked by {interaction.user.id} for: {reason}") await target.kick(f"Kicked by {interaction.user.id} for: {reason}")
await self.mysql_log(interaction.guild.id, interaction.user.id, 'KICK', target.id, 'NULL', reason) moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'KICK', target.id, 'NULL', reason)
await self.log(interaction, moderation_id)
@app_commands.command(name="ban") @app_commands.command(name="ban")
@app_commands.choices(delete_messages=[ @app_commands.choices(delete_messages=[
@ -565,7 +631,8 @@ class Moderation(commands.Cog):
except discord.errors.HTTPException: except discord.errors.HTTPException:
pass pass
await interaction.guild.ban(target, reason=f"Banned by {interaction.user.id} for: {reason}", delete_message_seconds=delete_messages) await interaction.guild.ban(target, reason=f"Banned by {interaction.user.id} for: {reason}", delete_message_seconds=delete_messages)
await self.mysql_log(interaction.guild.id, interaction.user.id, 'BAN', target.id, 'NULL', reason) moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'BAN', target.id, 'NULL', reason)
await self.log(interaction, moderation_id)
@app_commands.command(name="unban") @app_commands.command(name="unban")
async def unban(self, interaction: discord.Interaction, target: discord.User, reason: str = None, silent: bool = None): async def unban(self, interaction: discord.Interaction, target: discord.User, reason: str = None, silent: bool = None):
@ -610,7 +677,8 @@ class Moderation(commands.Cog):
await target.send(embed=embed) await target.send(embed=embed)
except discord.errors.HTTPException: except discord.errors.HTTPException:
pass pass
await self.mysql_log(interaction.guild.id, interaction.user.id, 'UNBAN', target.id, 'NULL', reason) moderation_id = await self.mysql_log(interaction.guild.id, interaction.user.id, 'UNBAN', target.id, 'NULL', reason)
await self.log(interaction, moderation_id)
@app_commands.command(name="history") @app_commands.command(name="history")
async def history(self, interaction: discord.Interaction, target: discord.User = None, moderator: discord.User = None, pagesize: app_commands.Range[int, 1, 25] = 5, page: int = 1, epheremal: bool = False): async def history(self, interaction: discord.Interaction, target: discord.User = None, moderator: discord.User = None, pagesize: app_commands.Range[int, 1, 25] = 5, page: int = 1, epheremal: bool = False):
@ -745,6 +813,7 @@ class Moderation(commands.Cog):
case_dict = self.generate_dict(result) case_dict = self.generate_dict(result)
embed = await self.embed_factory('case', interaction=interaction, case_dict=case_dict) embed = await self.embed_factory('case', interaction=interaction, case_dict=case_dict)
await interaction.response.send_message(content=f"✅ Moderation #{case_number} resolved!", embed=embed) await interaction.response.send_message(content=f"✅ Moderation #{case_number} resolved!", embed=embed)
await self.log(interaction, case_number, True)
cursor.close() cursor.close()
database.close() database.close()
@ -762,19 +831,13 @@ class Moderation(commands.Cog):
if permissions: if permissions:
await interaction.response.send_message(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True) await interaction.response.send_message(f"I do not have the `{permissions}` permission, required for this action.", ephemeral=True)
return return
database = await self.connect() if case_number != 0:
cursor = database.cursor() case = await self.fetch_case(case_number, interaction.guild.id)
query = "SELECT * FROM moderation_%s WHERE moderation_id = %s;" if case:
cursor.execute(query, (interaction.guild.id, case_number)) embed = await self.embed_factory('case', interaction=interaction, case_dict=case)
result = cursor.fetchone() await interaction.response.send_message(embed=embed, ephemeral=ephemeral)
cursor.close() return
database.close() await interaction.response.send_message(content=f"No case with case number `{case_number}` found.", ephemeral=True)
if result and case_number != 0:
case = self.generate_dict(result)
embed = await self.embed_factory('case', interaction=interaction, case_dict=case)
await interaction.response.send_message(embed=embed, ephemeral=ephemeral)
else:
await interaction.response.send_message(content=f"No case with case number `{case_number}` found.", ephemeral=True)
@tasks.loop(minutes=1) @tasks.loop(minutes=1)
async def handle_expiry(self): async def handle_expiry(self):
@ -830,6 +893,17 @@ class Moderation(commands.Cog):
await self.config.guild(ctx.guild).dm_users.set(not await self.config.guild(ctx.guild).dm_users()) await self.config.guild(ctx.guild).dm_users.set(not await self.config.guild(ctx.guild).dm_users())
await ctx.send(f"DM users setting set to {await self.config.guild(ctx.guild).dm_users()}") await ctx.send(f"DM users setting set to {await self.config.guild(ctx.guild).dm_users()}")
@moderationset.command(name="logchannel")
@checks.admin()
async def moderationset_logchannel(self, ctx: commands.Context, channel: discord.TextChannel = None):
"""Set a channel to log infractions to."""
if channel:
await self.config.guild(ctx.guild).log_channel.set(channel.id)
await ctx.send(f"Logging channel set to {channel.mention}.")
else:
await self.config.guild(ctx.guild).log_channel.set(" ")
await ctx.send(f"Logging channel disabled.")
@moderationset.command(name="mysql") @moderationset.command(name="mysql")
@checks.is_owner() @checks.is_owner()
async def moderationset_mysql(self, ctx: commands.Context): async def moderationset_mysql(self, ctx: commands.Context):