feat(aurora): boilerplate for all currently added moderation types
This commit is contained in:
parent
a05e957dde
commit
8f0425456c
4 changed files with 201 additions and 21 deletions
|
@ -9,7 +9,9 @@ from redbot.core import commands, data_manager
|
|||
from redbot.core.utils.chat_formatting import warning
|
||||
|
||||
from ..models.moderation import Moderation
|
||||
from ..models.type import Type
|
||||
from ..utilities.json import dump
|
||||
from ..utilities.registry import type_registry
|
||||
from ..utilities.utils import create_guild_table, timedelta_from_string
|
||||
|
||||
|
||||
|
@ -40,23 +42,18 @@ class ImportAuroraView(ui.View):
|
|||
file = await self.ctx.message.attachments[0].read()
|
||||
data: list[dict] = sorted(json.loads(file), key=lambda x: x["moderation_id"])
|
||||
|
||||
user_mod_types = ["NOTE", "WARN", "ADDROLE", "REMOVEROLE", "MUTE", "UNMUTE", "KICK", "TEMPBAN", "BAN", "UNBAN"]
|
||||
|
||||
channel_mod_types = ["SLOWMODE", "LOCKDOWN"]
|
||||
|
||||
failed_cases = []
|
||||
|
||||
for case in data:
|
||||
if case["moderation_id"] == 0:
|
||||
continue
|
||||
|
||||
moderation_type: Type = type_registry[str.lower(case["moderation_type"])]
|
||||
if "target_type" not in case or not case["target_type"]:
|
||||
if case["moderation_type"] in user_mod_types:
|
||||
case["target_type"] = "USER"
|
||||
elif case["moderation_type"] in channel_mod_types:
|
||||
case["target_type"] = "CHANNEL"
|
||||
if moderation_type.channel:
|
||||
case["target_type"] = "channel"
|
||||
else:
|
||||
case["target_type"] = "USER"
|
||||
case["target_type"] = "user"
|
||||
|
||||
if "role_id" not in case or not case["role_id"]:
|
||||
case["role_id"] = None
|
||||
|
@ -95,8 +92,8 @@ class ImportAuroraView(ui.View):
|
|||
bot=interaction.client,
|
||||
guild_id=self.ctx.guild.id,
|
||||
moderator_id=case["moderator_id"],
|
||||
moderation_type=case["moderation_type"],
|
||||
target_type=case["target_type"],
|
||||
moderation_type=moderation_type.key,
|
||||
target_type=case["target_type"].lower(),
|
||||
target_id=case["target_id"],
|
||||
role_id=case["role_id"],
|
||||
duration=duration,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
|
||||
from discord import File, Guild, Member, User
|
||||
from math import ceil
|
||||
|
||||
from discord import File, Guild, Member, TextChannel, User
|
||||
from discord.abc import Messageable
|
||||
from discord.errors import HTTPException, NotFound
|
||||
from redbot.core import app_commands, commands
|
||||
from redbot.core.bot import Red
|
||||
|
@ -19,6 +22,86 @@ def get_icon(bot: Red) -> File:
|
|||
return get_footer_image(cog)
|
||||
raise ValueError("Aurora cog not found. How was this managed?")
|
||||
|
||||
@type_registry.register(key="note")
|
||||
class Note(Type):
|
||||
key="note"
|
||||
string="note"
|
||||
verb="noted"
|
||||
|
||||
@type_registry.register(key="warn")
|
||||
class Warn(Type):
|
||||
key="warn"
|
||||
string="warn"
|
||||
verb="warned"
|
||||
|
||||
@type_registry.register(key="addrole")
|
||||
class AddRole(Type):
|
||||
key="addrole"
|
||||
string="addrole"
|
||||
verb="added a role to"
|
||||
|
||||
@type_registry.register(key="removerole")
|
||||
class RemoveRole(Type):
|
||||
key="removerole"
|
||||
string="removerole"
|
||||
verb="removed a role from"
|
||||
|
||||
@type_registry.register(key="mute")
|
||||
class Mute(Type):
|
||||
key="mute"
|
||||
string="mute"
|
||||
verb="muted"
|
||||
|
||||
@type_registry.register(key="unmute")
|
||||
class Unmute(Type):
|
||||
key="unmute"
|
||||
string="unmute"
|
||||
verb="unmuted"
|
||||
|
||||
@type_registry.register(key="kick")
|
||||
class Kick(Type):
|
||||
key="kick"
|
||||
string="kick"
|
||||
verb="kicked"
|
||||
|
||||
@classmethod
|
||||
async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str = None) -> 'Kick':
|
||||
"""Kick a user."""
|
||||
bot = ctx.bot
|
||||
response_message = await ctx.send(f"{target.mention} has been {cls.verb}!\n{bold('Reason:')} {inline(reason)}")
|
||||
|
||||
if silent is False:
|
||||
try:
|
||||
embed = await message_factory(
|
||||
bot,
|
||||
await bot.get_embed_color(ctx.channel),
|
||||
ctx.guild,
|
||||
reason,
|
||||
cls(),
|
||||
ctx.author,
|
||||
None,
|
||||
response_message
|
||||
)
|
||||
await target.send(embed=embed, file=get_icon(bot))
|
||||
except HTTPException:
|
||||
pass
|
||||
|
||||
await target.kick(reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}")
|
||||
moderation = await Moderation.log(
|
||||
bot,
|
||||
ctx.guild.id,
|
||||
ctx.author.id,
|
||||
cls(),
|
||||
'user',
|
||||
target.id,
|
||||
None,
|
||||
None,
|
||||
reason
|
||||
)
|
||||
await response_message.edit(content=f"{target.mention} has been {cls.verb}! (Case {inline(f'#{moderation.id}')})\n{bold('Reason:')} {inline(reason)}")
|
||||
await log(ctx, moderation.id)
|
||||
await send_evidenceformat(ctx, moderation.id)
|
||||
return cls
|
||||
@type_registry.register(key="ban")
|
||||
class Ban(Type):
|
||||
key="ban"
|
||||
|
@ -31,7 +114,7 @@ class Ban(Type):
|
|||
bot = ctx.bot
|
||||
try:
|
||||
await ctx.guild.fetch_ban(target)
|
||||
await ctx.send(content=error(f"{target.mention} is already banned!"), ephemeral=True)
|
||||
await ctx.send(content=error(f"{target.mention} is already {cls.verb}!"), ephemeral=True)
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
|
@ -58,7 +141,7 @@ class Ban(Type):
|
|||
except HTTPException:
|
||||
pass
|
||||
|
||||
await ctx.guild.ban(target, reason=f"Banned by {ctx.author.id} for: {reason}", delete_message_seconds=delete_messages_seconds)
|
||||
await ctx.guild.ban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}", delete_message_seconds=delete_messages_seconds)
|
||||
moderation = await Moderation.log(
|
||||
bot,
|
||||
ctx.guild.id,
|
||||
|
@ -110,7 +193,7 @@ class Tempban(Ban):
|
|||
bot = ctx.bot
|
||||
try:
|
||||
await ctx.guild.fetch_ban(target)
|
||||
await ctx.send(content=error(f"{target.mention} is already banned!"), ephemeral=True)
|
||||
await ctx.send(content=error(f"{target.mention} is already {Ban.verb}!"), ephemeral=True)
|
||||
except NotFound:
|
||||
pass
|
||||
|
||||
|
@ -145,7 +228,7 @@ class Tempban(Ban):
|
|||
except HTTPException:
|
||||
pass
|
||||
|
||||
await ctx.guild.ban(target, reason=f"Tempbanned by {ctx.author.id} for: {reason} (Duration: {parsed_time})", delete_message_seconds=delete_messages_seconds)
|
||||
await ctx.guild.ban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason} (Duration: {parsed_time})", delete_message_seconds=delete_messages_seconds)
|
||||
moderation = await Moderation.log(
|
||||
bot,
|
||||
ctx.guild.id,
|
||||
|
@ -161,3 +244,101 @@ class Tempban(Ban):
|
|||
await log(ctx, moderation.id)
|
||||
await send_evidenceformat(ctx, moderation.id)
|
||||
return cls
|
||||
|
||||
@type_registry.register(key="unban")
|
||||
class Unban(Type):
|
||||
key="unban"
|
||||
string="unban"
|
||||
verb="unbanned"
|
||||
|
||||
@classmethod
|
||||
async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, reason: str = None) -> 'Unban':
|
||||
"""Unban a user."""
|
||||
bot = ctx.bot
|
||||
try:
|
||||
await ctx.guild.fetch_ban(target)
|
||||
except NotFound:
|
||||
await ctx.send(content=error(f"{target.mention} is not {Ban.verb}!"), ephemeral=True)
|
||||
return
|
||||
|
||||
response_message = await ctx.send(f"{target.mention} has been {cls.verb}!\n{bold('Reason:')} {inline(reason)}")
|
||||
|
||||
if silent is False:
|
||||
try:
|
||||
embed = await message_factory(
|
||||
bot,
|
||||
await bot.get_embed_color(ctx.channel),
|
||||
ctx.guild,
|
||||
reason,
|
||||
cls(),
|
||||
ctx.author,
|
||||
None,
|
||||
response_message
|
||||
)
|
||||
await target.send(embed=embed, file=get_icon(bot))
|
||||
except HTTPException:
|
||||
pass
|
||||
|
||||
await ctx.guild.unban(target, reason=f"{str.title(cls.verb)} by {ctx.author.id} for: {reason}")
|
||||
moderation = await Moderation.log(
|
||||
bot,
|
||||
ctx.guild.id,
|
||||
ctx.author.id,
|
||||
cls(),
|
||||
'user',
|
||||
target.id,
|
||||
None,
|
||||
None,
|
||||
reason
|
||||
)
|
||||
await response_message.edit(content=f"{target.mention} has been {cls.verb}! (Case {inline(f'#{moderation.id}')})\n{bold('Reason:')} {inline(reason)}")
|
||||
await log(ctx, moderation.id)
|
||||
await send_evidenceformat(ctx, moderation.id)
|
||||
return cls
|
||||
|
||||
@type_registry.register(key="slowmode")
|
||||
class Slowmode(Type):
|
||||
key="slowmode"
|
||||
string="slowmode"
|
||||
verb="set the slowmode in"
|
||||
channel=True
|
||||
|
||||
@classmethod
|
||||
async def handler(cls, ctx: commands.Context, target: Messageable, silent: bool, duration: str, reason: str) -> 'Slowmode': # pylint: disable=unused-argument
|
||||
"""Set the slowmode in a channel."""
|
||||
bot = ctx.bot
|
||||
parsed_time = parse_relativedelta(duration)
|
||||
if not parsed_time:
|
||||
await ctx.send(content=error("Please provide a valid duration!"), ephemeral=True)
|
||||
try:
|
||||
parsed_time = timedelta_from_relativedelta(parsed_time)
|
||||
except ValueError:
|
||||
await ctx.send(content=error("Please provide a valid duration!"), ephemeral=True)
|
||||
|
||||
if ceil(parsed_time.total_seconds()) > 21600:
|
||||
await ctx.send(content=error("The slowmode duration cannot exceed 6 hours!"), ephemeral=True)
|
||||
return
|
||||
|
||||
if isinstance(target, TextChannel):
|
||||
await target.edit(slowmode_delay=ceil(parsed_time.total_seconds()))
|
||||
moderation = await Moderation.log(
|
||||
bot,
|
||||
ctx.guild.id,
|
||||
ctx.author.id,
|
||||
cls(),
|
||||
'channel',
|
||||
target.id,
|
||||
None,
|
||||
parsed_time,
|
||||
None
|
||||
)
|
||||
await ctx.send(f"{ctx.author.mention} has {cls.verb} {target.mention} to {humanize_timedelta(parsed_time)}!\n{bold('Reason:')} {inline(reason)}")
|
||||
await log(ctx, moderation.id)
|
||||
return cls
|
||||
|
||||
@type_registry.register(key="lockdown")
|
||||
class Lockdown(Type):
|
||||
key="lockdown"
|
||||
string="lockdown"
|
||||
verb="locked down"
|
||||
channel=True
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from typing import Any
|
||||
|
||||
from discord import Guild, Member, User
|
||||
from discord.abc import Messageable
|
||||
from redbot.core import commands
|
||||
from redbot.core.bot import Red
|
||||
|
||||
|
@ -12,23 +13,24 @@ class Type(object):
|
|||
string = "type"
|
||||
verb = "typed"
|
||||
embed_desc = "been"
|
||||
channel = False # if this is True, the overridden handler methods should be typed with `discord.abc.Messageable` instead of `discord.Member | discord.User`
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.string
|
||||
|
||||
@classmethod
|
||||
async def handler(cls, ctx: commands.Context, target: Member | User, silent: bool, **kwargs) -> 'Type': # pylint: disable=unused-argument
|
||||
async def handler(cls, ctx: commands.Context, target: Member | User | Messageable, silent: bool, **kwargs) -> 'Type': # pylint: disable=unused-argument
|
||||
"""This method should be overridden by any child classes, but should retain the same starting keyword arguments."""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
async def resolve_handler(cls, bot: Red, guild: Guild, target: Member | User, reason: str | None = None) -> Any: # pylint: disable=unused-argument
|
||||
async def resolve_handler(cls, bot: Red, guild: Guild, target: Member | User | Messageable, reason: str | None = None, **kwargs) -> Any: # pylint: disable=unused-argument
|
||||
"""This method should be overridden by any resolvable child classes, but should retain the same keyword arguments.
|
||||
If your moderation type should not be resolvable, do not override this."""
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
async def expiry_handler(cls, bot: Red, guild: Guild, target: Member | User) -> Any: # pylint: disable=unused-argument
|
||||
async def expiry_handler(cls, bot: Red, guild: Guild, target: Member | User | Messageable, **kwargs) -> Any: # pylint: disable=unused-argument
|
||||
"""This method should be overridden by any expirable child classes, but should retain the same keyword arguments.
|
||||
If your moderation type should not expire, do not override this."""
|
||||
raise NotImplementedError
|
||||
|
|
|
@ -9,14 +9,14 @@ from .registry import type_registry
|
|||
from .utils import check_moddable
|
||||
|
||||
|
||||
async def moderate(ctx: Union[commands.Context, discord.Interaction], target: discord.Member, silent: bool | None, permissions: List[str], moderation_type: Type | str, **kwargs) -> None | Type:
|
||||
async def moderate(ctx: Union[commands.Context, discord.Interaction], target: discord.Member | discord.User | discord.abc.Messageable, silent: bool | None, permissions: List[str], moderation_type: Type | str, **kwargs) -> None | Type:
|
||||
"""This function is used to moderate users.
|
||||
It checks if the target can be moderated, then calls the handler method of the moderation type specified.
|
||||
|
||||
Args:
|
||||
bot (Red): The bot instance.
|
||||
ctx (Union[commands.Context, discord.Interaction]): The context of the command. If this is a `discord.Interaction` object, it will be converted to a `commands.Context` object. Additionally, if the interaction orignated from a context menu, the `ctx.author` attribute will be overriden to `interaction.user`.
|
||||
target (discord.Member): The target user to moderate.
|
||||
target (discord.Member, discord.User, discord.abc.Messageable): The target user or channel to moderate.
|
||||
silent (bool | None): Whether to send the moderation action to the target.
|
||||
permissions (List[str]): The permissions required to moderate the target.
|
||||
moderation_type (Type): The moderation type (handler) to use. See `aurora.models.moderation_types` for some examples.
|
||||
|
|
Loading…
Reference in a new issue