async def post_formatted_message( self, actor: User, action: str, *, body: Optional[str] = None, link: Optional[str] = None, colour: int = Colours.green, ) -> None: """Format and post a message to the #log channel.""" logger.trace(f'Creating log "{actor.id} {action}"') embed = Embed( title=( f"{actor} " f"{f'({actor.display_name}) ' if actor.display_name != actor.name else ''}" f"({actor.id}) {action}" ), description=body or "<no additional information provided>", colour=colour, timestamp=datetime.utcnow(), ).set_thumbnail(url=actor.display_avatar.url) if link: embed.url = link await self.post_message(embed=embed)
async def paginate( cls, lines: t.List[str], ctx: Context, embed: disnake.Embed, prefix: str = "", suffix: str = "", max_lines: t.Optional[int] = None, max_size: int = 500, scale_to_size: int = 2000, empty: bool = False, restrict_to_user: User = None, timeout: int = 300, footer_text: str = None, url: str = None, allow_empty_lines: bool = False, ) -> t.Optional[disnake.Message]: """ Use a paginator and set of reactions to provide pagination over a set of lines. When used, this will send a message using `ctx.send()` and apply the pagination reactions, to control the embed. Pagination will also be removed automatically if no reaction is added for `timeout` seconds. The interaction will be limited to `restrict_to_user` (ctx.author by default) or to any user with a moderation role. Example: >>> people = ["Guido van Rossum", "Linus Torvalds", "Gurkbot", "Bjarne Stroustrup"] >>> e = disnake.Embed() >>> e.set_author(name="Ain't these people just awesome?") >>> await LinePaginator.paginate(people, ctx, e) """ def event_check(reaction_: disnake.Reaction, user_: disnake.Member) -> bool: """Make sure that this reaction is what we want to operate on.""" return ( # Conditions for a successful pagination: all(( # Reaction is on this message reaction_.message.id == message.id, # Reaction is one of the pagination emotes str(reaction_.emoji) in PAGINATION_EMOJI, # Reaction was not made by the Bot user_.id != ctx.bot.user.id, # The reaction was by a whitelisted user user_.id == restrict_to_user.id, ))) paginator = cls( prefix=prefix, suffix=suffix, max_size=max_size, max_lines=max_lines, scale_to_size=scale_to_size, ) current_page = 0 # If the `restrict_to_user` is empty then set it to the original message author. restrict_to_user = restrict_to_user or ctx.author if not lines: if not allow_empty_lines: logger.exception( "`Empty lines found, raising error as `allow_empty_lines` is `False`." ) raise EmptyPaginatorEmbed("No lines to paginate.") logger.debug( "Empty lines found, `allow_empty_lines` is `True`, adding 'nothing to display' as content." ) lines.append("(nothing to display)") for line in lines: try: paginator.add_line(line, empty=empty) except Exception: logger.exception(f"Failed to add line to paginator: '{line}'.") raise logger.debug(f"Paginator created with {len(paginator.pages)} pages.") # Set embed description to content of current page. embed.description = paginator.pages[current_page] if len(paginator.pages) <= 1: if footer_text: embed.set_footer(text=footer_text) if url: embed.url = url logger.debug("Less than two pages, skipping pagination.") await ctx.send(embed=embed) return else: if footer_text: embed.set_footer( text= f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})" ) else: embed.set_footer( text=f"Page {current_page + 1}/{len(paginator.pages)}") if url: embed.url = url message = await ctx.send(embed=embed) logger.debug("Adding emoji reactions to message...") for emoji in PAGINATION_EMOJI: # Add all the applicable emoji to the message await message.add_reaction(emoji) logger.debug("Successfully added all pagination emojis to message.") while True: try: reaction, user = await ctx.bot.wait_for("reaction_add", timeout=timeout, check=event_check) logger.trace(f"Got reaction: {reaction}.") except asyncio.TimeoutError: logger.debug("Timed out waiting for a reaction.") break # We're done, no reactions for the last 5 minutes if str(reaction.emoji) == DELETE_EMOJI: logger.debug("Got delete reaction.") await message.delete() return if reaction.emoji == FIRST_EMOJI: await message.remove_reaction(reaction.emoji, user) current_page = 0 logger.debug( f"Got first page reaction - changing to page 1/{len(paginator.pages)}." ) embed.description = paginator.pages[current_page] if footer_text: # Current page is zero index based. embed.set_footer( text= f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})" ) else: embed.set_footer( text=f"Page {current_page + 1}/{len(paginator.pages)}") await message.edit(embed=embed) if reaction.emoji == LAST_EMOJI: await message.remove_reaction(reaction.emoji, user) current_page = len(paginator.pages) - 1 logger.debug( f"Got last page reaction - changing to page {current_page + 1}/{len(paginator.pages)}" ) embed.description = paginator.pages[current_page] if footer_text: embed.set_footer( text= f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})" ) else: embed.set_footer( text=f"Page {current_page + 1}/{len(paginator.pages)}") await message.edit(embed=embed) if reaction.emoji == LEFT_EMOJI: await message.remove_reaction(reaction.emoji, user) if current_page <= 0: logger.debug( "Got previous page reaction while they are on the first page, ignoring." ) continue current_page -= 1 logger.debug( f"Got previous page reaction - changing to page {current_page + 1}/{len(paginator.pages)}" ) embed.description = paginator.pages[current_page] if footer_text: embed.set_footer( text= f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})" ) else: embed.set_footer( text=f"Page {current_page + 1}/{len(paginator.pages)}") await message.edit(embed=embed) if reaction.emoji == RIGHT_EMOJI: await message.remove_reaction(reaction.emoji, user) if current_page >= len(paginator.pages) - 1: logger.debug( "Got next page reaction while they are on the last page, ignoring." ) continue current_page += 1 logger.debug( f"Got next page reaction - changing to page {current_page + 1}/{len(paginator.pages)}" ) embed.description = paginator.pages[current_page] if footer_text: embed.set_footer( text= f"{footer_text} (Page {current_page + 1}/{len(paginator.pages)})" ) else: embed.set_footer( text=f"Page {current_page + 1}/{len(paginator.pages)}") await message.edit(embed=embed) logger.debug("Ending pagination and clearing reactions.") with suppress(disnake.NotFound): await message.clear_reactions()