diff options
Diffstat (limited to 'bot/exts/utilities/bookmark.py')
-rw-r--r-- | bot/exts/utilities/bookmark.py | 123 |
1 files changed, 73 insertions, 50 deletions
diff --git a/bot/exts/utilities/bookmark.py b/bot/exts/utilities/bookmark.py index b50205a0..2e3458d8 100644 --- a/bot/exts/utilities/bookmark.py +++ b/bot/exts/utilities/bookmark.py @@ -16,6 +16,13 @@ log = logging.getLogger(__name__) # Number of seconds to wait for other users to bookmark the same message TIMEOUT = 120 BOOKMARK_EMOJI = "📌" +MESSAGE_NOT_FOUND_ERROR = ( + "You must either provide a reference to a valid message, or reply to one." + "\n\nThe lookup strategy for a message is as follows (in order):" + "\n1. Lookup by '{channel ID}-{message ID}' (retrieved by shift-clicking on 'Copy ID')" + "\n2. Lookup by message ID (the message **must** be in the current channel)" + "\n3. Lookup by message URL" +) class Bookmark(commands.Cog): @@ -42,51 +49,37 @@ class Bookmark(commands.Cog): return embed @staticmethod - def build_error_embed(user: discord.Member) -> discord.Embed: - """Builds an error embed for when a bookmark requester has DMs disabled.""" + def build_error_embed(message: str) -> discord.Embed: + """Builds an error embed for a given message.""" return discord.Embed( title=random.choice(ERROR_REPLIES), - description=f"{user.mention}, please enable your DMs to receive the bookmark.", + description=message, colour=Colours.soft_red ) async def action_bookmark( self, channel: discord.TextChannel, - user: discord.Member, + member: discord.Member, target_message: discord.Message, title: str ) -> None: - """Sends the bookmark DM, or sends an error embed when a user bookmarks a message.""" + """ + Sends the given target_message as a bookmark to the member in DMs to the user. + + Send an error embed instead if the member has DMs disabled. + """ + embed = self.build_bookmark_dm(target_message, title) try: - embed = self.build_bookmark_dm(target_message, title) - await user.send(embed=embed) + await member.send(embed=embed) except discord.Forbidden: - error_embed = self.build_error_embed(user) + error_embed = self.build_error_embed(f"{member.mention}, please enable your DMs to receive the bookmark.") await channel.send(embed=error_embed) else: - log.info(f"{user} bookmarked {target_message.jump_url} with title '{title}'") - - @staticmethod - async def send_reaction_embed( - channel: discord.TextChannel, - target_message: discord.Message - ) -> discord.Message: - """Sends an embed, with a reaction, so users can react to bookmark the message too.""" - message = await channel.send( - embed=discord.Embed( - description=( - f"React with {BOOKMARK_EMOJI} to be sent your very own bookmark to " - f"[this message]({target_message.jump_url})." - ), - colour=Colours.soft_green - ) - ) - - await message.add_reaction(BOOKMARK_EMOJI) - return message + log.info(f"{member} bookmarked {target_message.jump_url} with title '{title}'") - @commands.command(name="bookmark", aliases=("bm", "pin")) + @commands.group(name="bookmark", aliases=("bm", "pin"), invoke_without_command=True) + @commands.guild_only() @whitelist_override(roles=(Roles.everyone,)) async def bookmark( self, @@ -95,30 +88,40 @@ class Bookmark(commands.Cog): *, title: str = "Bookmark" ) -> None: - """Send the author a link to `target_message` via DMs.""" - if not target_message: - if not ctx.message.reference: - raise commands.UserInputError( - "You must either provide a valid message to bookmark, or reply to one." - "\n\nThe lookup strategy for a message is as follows (in order):" - "\n1. Lookup by '{channel ID}-{message ID}' (retrieved by shift-clicking on 'Copy ID')" - "\n2. Lookup by message ID (the message **must** be in the context channel)" - "\n3. Lookup by message URL" - ) - target_message = ctx.message.reference.resolved + """ + Send the author a link to the specified message via DMs. + + Members can either give a message as an argument, or reply to a message. + + Bookmarks can subsequently be deleted by using the `bookmark delete` command in DMs. + """ + target_message: Optional[discord.Message] = target_message or getattr(ctx.message.reference, "resolved", None) + if target_message is None: + raise commands.UserInputError(MESSAGE_NOT_FOUND_ERROR) # Prevent users from bookmarking a message in a channel they don't have access to permissions = target_message.channel.permissions_for(ctx.author) if not permissions.read_messages: log.info(f"{ctx.author} tried to bookmark a message in #{target_message.channel} but has no permissions.") - embed = discord.Embed( - title=random.choice(ERROR_REPLIES), - color=Colours.soft_red, - description="You don't have permission to view this channel." - ) + embed = self.build_error_embed(f"{ctx.author.mention} You don't have permission to view this channel.") await ctx.send(embed=embed) return + await self.action_bookmark(ctx.channel, ctx.author, target_message, title) + + # Keep track of who has already bookmarked, so users can't spam reactions and cause loads of DMs + bookmarked_users = [ctx.author.id] + + reaction_embed = discord.Embed( + description=( + f"React with {BOOKMARK_EMOJI} to be sent your very own bookmark to " + f"[this message]({ctx.message.jump_url})." + ), + colour=Colours.soft_green + ) + reaction_message = await ctx.send(embed=reaction_embed) + await reaction_message.add_reaction(BOOKMARK_EMOJI) + def event_check(reaction: discord.Reaction, user: discord.Member) -> bool: """Make sure that this reaction is what we want to operate on.""" return ( @@ -134,11 +137,6 @@ class Bookmark(commands.Cog): user.id != self.bot.user.id )) ) - await self.action_bookmark(ctx.channel, ctx.author, target_message, title) - - # Keep track of who has already bookmarked, so users can't spam reactions and cause loads of DMs - bookmarked_users = [ctx.author.id] - reaction_message = await self.send_reaction_embed(ctx.channel, target_message) while True: try: @@ -152,6 +150,31 @@ class Bookmark(commands.Cog): await reaction_message.delete() + @bookmark.command(name="delete", aliases=("del", "rm"), root_aliases=("unbm", "unbookmark", "dmdelete", "dmdel")) + @whitelist_override(bypass_defaults=True, allow_dm=True) + async def delete_bookmark( + self, + ctx: commands.Context, + ) -> None: + """ + Delete the Sir-Lancebot message that the command invocation is replying to. + + This command allows deleting any message sent by Sir-Lancebot in the user's DM channel with the bot. + The command invocation must be a reply to the message that is to be deleted. + """ + target_message: Optional[discord.Message] = getattr(ctx.message.reference, "resolved", None) + if target_message is None: + raise commands.UserInputError("You must reply to the message from Sir-Lancebot you wish to delete.") + + if not isinstance(ctx.channel, discord.DMChannel): + raise commands.UserInputError("You can only run this command your own DMs!") + elif target_message.channel != ctx.channel: + raise commands.UserInputError("You can only delete messages in your own DMs!") + elif target_message.author != self.bot.user: + raise commands.UserInputError("You can only delete messages sent by Sir Lancebot!") + + await target_message.delete() + def setup(bot: Bot) -> None: """Load the Bookmark cog.""" |