diff --git a/extensions/admin.py b/extensions/admin.py index 524d58e..7b0ae89 100644 --- a/extensions/admin.py +++ b/extensions/admin.py @@ -12,7 +12,7 @@ from hikari import Attachment, GatewayBot, Message, MessageFlag from loguru import logger -from core.formatters import Colors, ExpandUser, Response +from core.formatters import Colors, ExpandChannel, ExpandUser, Response from core.hooks import HookError, HookLog plugin: GatewayPlugin = GatewayPlugin("admin") @@ -37,6 +37,104 @@ def ExtensionLoader(client: GatewayClient) -> None: logger.opt(exception=e).error(f"Failed to load {plugin.name} extension") +@plugin.include +@arc.with_hook(arc.owner_only) +@arc.with_hook(HookLog) +@arc.slash_command( + "edit", + "Edit a message sent by N31L.", + is_dm_enabled=True, + autodefer=AutodeferMode.EPHEMERAL, +) +async def CommandEdit( + ctx: GatewayContext, + channelId: Option[ + str, StrParams("Enter the ID of the channel.", name="channel_id") + ], + messageId: Option[ + str, StrParams("Enter the ID of the message.", name="message_id") + ], + content: Option[ + str | None, StrParams("Enter text to replace the message content with.") + ] = None, + file: Option[ + Attachment | None, + AttachmentParams("Choose a text file containing markdown content."), + ] = None, +) -> None: + """Handler for the /edit command.""" + + if (not content) and (not file): + logger.debug( + "Edit Message command ignored, no message content or file provided" + ) + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + color=Colors.DiscordRed.value, + description=f"No message content provided.", + ), + ) + + return + + bot: GatewayBot = ctx.client.get_type_dependency(GatewayBot) + + if not (n31l := bot.get_me()): + raise RuntimeError("Bot user is null") + + msg: Message = await ctx.client.rest.fetch_message(int(channelId), int(messageId)) + + if not msg: + logger.debug( + f"Failed to fetch message {messageId} in channel {ExpandChannel(channelId, format=False)}" + ) + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + color=Colors.DiscordRed.value, description="Failed to fetch message." + ), + ) + + return + + if msg.author.id != n31l.id: + logger.debug("Edit Message command ignored, message author is not N31L") + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + color=Colors.DiscordRed.value, + description=f"Message author is not {n31l.mention}.", + ), + ) + + return + + before: str = "[Empty]" + after: str = "[Empty]" + + if content: + after = content + elif file: + after = (await file.read()).decode("UTF-8") + + if msg.content: + before = msg.content + + await msg.edit(after) + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + color=Colors.DiscordGreen.value, + description=f"Edited {msg.author.mention}'s message {msg.make_link(msg.guild_id)}.\n\n**Before:**\n```md\n{before}\n```\n**After:**\n```md\n{after}\n```", + ), + ) + + @plugin.include @arc.with_hook(arc.owner_only) @arc.with_hook(HookLog) @@ -58,7 +156,7 @@ async def CommandDelete(ctx: GatewayContext, msg: Message) -> None: flags=MessageFlag.EPHEMERAL, embed=Response( color=Colors.DiscordRed.value, - description=f"Message author is not {n31l.mention}.", + description=f"Message {msg.make_link(msg.guild_id)} author is not {n31l.mention}.", ), ) diff --git a/extensions/messages.py b/extensions/messages.py index 5c2d8d3..f9d806a 100644 --- a/extensions/messages.py +++ b/extensions/messages.py @@ -4,6 +4,8 @@ GatewayClient, GatewayContext, GatewayPlugin, + Option, + StrParams, ) from hikari import Message, MessageFlag, MessageType, Permissions from loguru import logger @@ -12,6 +14,7 @@ from core.formatters import ( Colors, ExpandChannel, + ExpandCommand, ExpandServer, ExpandUser, GetAvatar, @@ -37,6 +40,68 @@ def ExtensionLoader(client: GatewayClient) -> None: logger.opt(exception=e).error(f"Failed to load {plugin.name} extension") +@plugin.include +@arc.with_hook(HookLog) +@arc.slash_command( + "raw", + "Return the raw markdown content of a provided message.", + is_dm_enabled=True, + autodefer=AutodeferMode.EPHEMERAL, +) +async def CommandRawSlash( + ctx: GatewayContext, + channelId: Option[ + str, StrParams("Enter the ID of the channel.", name="channel_id") + ], + messageId: Option[ + str, StrParams("Enter the ID of the message.", name="message_id") + ], +) -> None: + """Handler for the /raw slash command.""" + + msg: Message = await ctx.client.rest.fetch_message(int(channelId), int(messageId)) + + if not msg: + logger.debug( + f"Failed to fetch message {messageId} in channel {ExpandChannel(channelId, format=False)}" + ) + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + color=Colors.DiscordRed.value, description="Failed to fetch message." + ), + ) + + return + + if not msg.content: + logger.debug( + f"Command {ExpandCommand(ctx, format=False)} ignored, message content is null" + ) + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + color=Colors.DiscordRed.value, + description=f"{msg.author.mention}'s message {msg.make_link(msg.guild_id)} has no content.", + ), + ) + + return + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + author=ExpandUser(msg.author, format=False), + authorIcon=GetAvatar(msg.author), + color=Colors.DiscordGreen.value, + description=f"{msg.make_link(msg.guild_id)}\n```md\n{msg.content}\n```", + timestamp=msg.timestamp, + ), + ) + + @plugin.include @arc.with_hook(arc.has_permissions(Permissions.MANAGE_MESSAGES)) @arc.with_hook(HookLog) @@ -121,6 +186,37 @@ async def CommandParse(ctx: GatewayContext, msg: Message) -> None: ) +@plugin.include +@arc.with_hook(HookLog) +@arc.message_command("Raw Message", autodefer=AutodeferMode.EPHEMERAL) +async def CommandRaw(ctx: GatewayContext, msg: Message) -> None: + """Handler for the Raw Message context menu command.""" + + if not msg.content: + logger.debug("Raw Message command ignored, message content is null") + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + color=Colors.DiscordRed.value, + description=f"{msg.author.mention}'s message {msg.make_link(msg.guild_id)} has no content.", + ), + ) + + return + + await ctx.respond( + flags=MessageFlag.EPHEMERAL, + embed=Response( + author=ExpandUser(msg.author, format=False), + authorIcon=GetAvatar(msg.author), + color=Colors.DiscordGreen.value, + description=f"{msg.make_link(msg.guild_id)}\n```md\n{msg.content}\n```", + timestamp=msg.timestamp, + ), + ) + + @plugin.include @arc.with_hook(HookLog) @arc.message_command("Report Message", autodefer=AutodeferMode.EPHEMERAL)