DevilXD 6e63ed4e60
Use aware objects when storing and reading UTC timestamps (#4017)
* Use aware objects instead of naive ones

* Use aware objects when storing and reading UTC timestamps

* Remove unneeded parentheses

* Fixed naive and aware objects unable to be compared here

* Address feedback

* Fix the newly added `modlog.create_case()` calls

Co-authored-by: jack1142 <6032823+jack1142@users.noreply.github.com>
2020-08-12 10:46:32 +02:00

190 lines
6.9 KiB
Python

from datetime import timezone
from typing import Optional, Union
import discord
from redbot.core import checks, modlog, commands
from redbot.core.bot import Red
from redbot.core.i18n import Translator, cog_i18n
from redbot.core.utils.chat_formatting import box
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS
_ = Translator("ModLog", __file__)
@cog_i18n(_)
class ModLog(commands.Cog):
"""Manage log channels for moderation actions."""
def __init__(self, bot: Red):
super().__init__()
self.bot = bot
async def red_delete_data_for_user(self, **kwargs):
""" Nothing to delete """
return
@commands.group()
@checks.guildowner_or_permissions(administrator=True)
async def modlogset(self, ctx: commands.Context):
"""Manage modlog settings."""
pass
@checks.is_owner()
@modlogset.command(hidden=True, name="fixcasetypes")
async def reapply_audittype_migration(self, ctx: commands.Context):
"""Command to fix misbehaving casetypes."""
await modlog.handle_auditype_key()
await ctx.tick()
@modlogset.command()
@commands.guild_only()
async def modlog(self, ctx: commands.Context, channel: discord.TextChannel = None):
"""Set a channel as the modlog.
Omit `<channel>` to disable the modlog.
"""
guild = ctx.guild
if channel:
if channel.permissions_for(guild.me).send_messages:
await modlog.set_modlog_channel(guild, channel)
await ctx.send(
_("Mod events will be sent to {channel}.").format(channel=channel.mention)
)
else:
await ctx.send(
_("I do not have permissions to send messages in {channel}!").format(
channel=channel.mention
)
)
else:
try:
await modlog.get_modlog_channel(guild)
except RuntimeError:
await ctx.send(_("Mod log is already disabled."))
else:
await modlog.set_modlog_channel(guild, None)
await ctx.send(_("Mod log deactivated."))
@modlogset.command(name="cases")
@commands.guild_only()
async def set_cases(self, ctx: commands.Context, action: str = None):
"""Enable or disable case creation for a mod action."""
guild = ctx.guild
if action is None: # No args given
casetypes = await modlog.get_all_casetypes(guild)
await ctx.send_help()
lines = []
for ct in casetypes:
enabled = _("enabled") if await ct.is_enabled() else _("disabled")
lines.append(f"{ct.name} : {enabled}")
await ctx.send(_("Current settings:\n") + box("\n".join(lines)))
return
casetype = await modlog.get_casetype(action, guild)
if not casetype:
await ctx.send(_("That action is not registered."))
else:
enabled = await casetype.is_enabled()
await casetype.set_enabled(not enabled)
await ctx.send(
_("Case creation for {action_name} actions is now {enabled}.").format(
action_name=action, enabled=_("enabled") if not enabled else _("disabled")
)
)
@modlogset.command()
@commands.guild_only()
async def resetcases(self, ctx: commands.Context):
"""Reset all modlog cases in this server."""
guild = ctx.guild
await modlog.reset_cases(guild)
await ctx.send(_("Cases have been reset."))
@commands.command()
@commands.guild_only()
async def case(self, ctx: commands.Context, number: int):
"""Show the specified case."""
try:
case = await modlog.get_case(number, ctx.guild, self.bot)
except RuntimeError:
await ctx.send(_("That case does not exist for that server."))
return
else:
if await ctx.embed_requested():
await ctx.send(embed=await case.message_content(embed=True))
else:
await ctx.send(await case.message_content(embed=False))
@commands.command()
@commands.guild_only()
async def casesfor(self, ctx: commands.Context, *, member: Union[discord.Member, int]):
"""Display cases for the specified member."""
try:
if isinstance(member, int):
cases = await modlog.get_cases_for_member(
bot=ctx.bot, guild=ctx.guild, member_id=member
)
else:
cases = await modlog.get_cases_for_member(
bot=ctx.bot, guild=ctx.guild, member=member
)
except discord.NotFound:
return await ctx.send(_("That user does not exist."))
except discord.HTTPException:
return await ctx.send(
_("Something unexpected went wrong while fetching that user by ID.")
)
if not cases:
return await ctx.send(_("That user does not have any cases."))
embed_requested = await ctx.embed_requested()
rendered_cases = [await case.message_content(embed=embed_requested) for case in cases]
await menu(ctx, rendered_cases, DEFAULT_CONTROLS)
@commands.command()
@commands.guild_only()
async def reason(self, ctx: commands.Context, case: Optional[int], *, reason: str):
"""Specify a reason for a modlog case.
Please note that you can only edit cases you are
the owner of unless you are a mod, admin or server owner.
If no case number is specified, the latest case will be used.
"""
author = ctx.author
guild = ctx.guild
if case is None:
# get the latest case
case_obj = await modlog.get_latest_case(guild, self.bot)
if case_obj is None:
await ctx.send(_("There are no modlog cases in this server."))
return
else:
try:
case_obj = await modlog.get_case(case, guild, self.bot)
except RuntimeError:
await ctx.send(_("That case does not exist!"))
return
is_guild_owner = author == guild.owner
is_case_author = author == case_obj.moderator
author_is_mod = await ctx.bot.is_mod(author)
if not (is_guild_owner or is_case_author or author_is_mod):
await ctx.send(_("You are not authorized to modify that case!"))
return
to_modify = {"reason": reason}
if case_obj.moderator != author:
to_modify["amended_by"] = author
to_modify["modified_at"] = ctx.message.created_at.replace(tzinfo=timezone.utc).timestamp()
await case_obj.edit(to_modify)
await ctx.send(
_("Reason for case #{num} has been updated.").format(num=case_obj.case_number)
)