diff --git a/examples/views/paginator.py b/examples/views/paginator.py new file mode 100644 index 00000000..0101cc0b --- /dev/null +++ b/examples/views/paginator.py @@ -0,0 +1,210 @@ +import discord +from discord.ext import commands +from discord import ButtonStyle, Embed, Interaction +from discord.ui import Button, View + + +class Bot(commands.Bot): + def __init__(self): + super().__init__( + command_prefix=commands.when_mentioned_or("$"), intents=discord.Intents(guilds=True, messages=True) + ) + + async def on_ready(self): + print(f"Logged in as {self.user} (ID: {self.user.id})") + print("------") + + +# Define 3 View subclasses that we will switch between depending on if we are viewing the first page, a page in the middle, or the last page. +# The first page has the "left" button disabled, because there is no left page to go to +class FirstPageComps(View): + def __init__( + self, + author_id: int, + ): + super().__init__() + self.value = None + self.author_id = author_id + + async def interaction_check( + self, + interaction: Interaction, + ): + return interaction.user.id == self.author_id + + @discord.ui.button( + style=ButtonStyle.primary, + disabled=True, + label="Left", + ) + async def left( + self, + button: discord.ui.Button, + interaction: Interaction, + ): + self.value = "left" + self.stop() + + @discord.ui.button( + style=ButtonStyle.primary, + label="Right", + ) + async def right( + self, + button: discord.ui.Button, + interaction: Interaction, + ): + self.value = "right" + self.stop() + + +# The middle pages have both left and right buttons available +class MiddlePageComps(View): + def __init__( + self, + author_id: int, + ): + super().__init__() + self.value = None + self.author_id = author_id + + async def interaction_check( + self, + interaction: Interaction, + ): + return interaction.user.id == self.author_id + + @discord.ui.button( + style=ButtonStyle.primary, + label="Left", + ) + async def left( + self, + button: discord.ui.Button, + interaction: Interaction, + ): + self.value = "left" + self.stop() + + @discord.ui.button( + style=ButtonStyle.primary, + label="Right", + ) + async def right( + self, + button: discord.ui.Button, + interaction: Interaction, + ): + self.value = "right" + self.stop() + + +# The last page has the right button disabled, because there's no right page to go to +class LastPageComps(View): + def __init__( + self, + author_id: int, + ): + super().__init__() + self.value = None + self.author_id = author_id + + async def interaction_check( + self, + interaction: Interaction, + ): + return interaction.user.id == self.author_id + + @discord.ui.button( + style=ButtonStyle.primary, + label="Left", + ) + async def left( + self, + button: discord.ui.Button, + interaction: Interaction, + ): + self.value = "left" + self.stop() + + @discord.ui.button( + style=ButtonStyle.primary, + label="Right", + disabled=True, + ) + async def right( + self, + button: discord.ui.Button, + interaction: Interaction, + ): + self.value = "right" + self.stop() + + +# Now we define the function that will take care of the pagination for us. It will take a list of Embeds and allow the user to cycle between them using left/right buttons +# There is also an optional title parameter in case you want to give your paginator a title, it will display in the embed title, for example if the title is Book +# then the title will look like "Book | Page 1/2". This is very optional and can be removed +async def paginate( + ctx, + pages: list, + title: str = None, +): + + total_pages = len(pages) + first_page = pages[0] + last_page = pages[-1] + current_page = first_page + index = 0 + + embed = first_page + if title: + embed.title = f"{title} | Page {index+1}/{total_pages}" + + view = FirstPageComps(ctx.author.id) + # Here we send the message with the view of the first page and the FirstPageComps buttons + msg = await ctx.send( + embed=embed, + view=view, + ) + + # The default timeout for Views is 3 minutes, but if a user selects to scroll between pages the timer will be reset. + # If the timer reaches 0, though, the loop will just silently stop. + while True: + wait = await view.wait() + if wait: + return + + if view.value == "right": + index += 1 + current_page = pages[index] + view = MiddlePageComps(ctx.author.id) if current_page != last_page else LastPageComps(ctx.author.id) + embed = current_page + if title: + embed.title = f"{title} | Page {index+1}/{total_pages}" + + elif view.value == "left": + index -= 1 + current_page = pages[index] + view = MiddlePageComps(ctx.author.id) if current_page != first_page else FirstPageComps(ctx.author.id) + embed = current_page + if title: + embed.title = f"{title} | Page {index+1}/{total_pages}" + + await msg.edit( + embed=embed, + view=view, + ) + + +bot = Bot() + +# To test our new function, let's create a list of a couple Embeds and let our paginator deal with the sending and buttons +@bot.command() +async def sendpages(ctx): + page1 = Embed(description="This is page 1") + page2 = Embed(description="This is page 2") + page3 = Embed(description="This is page 3") + await paginate(ctx, [page1, page2, page3]) + + +bot.run("token")