Skip to content

Instantly share code, notes, and snippets.

@mudkipdev
Last active August 13, 2023 19:04
Show Gist options
  • Save mudkipdev/4b0ce96a2d9bd6205e16c52798546571 to your computer and use it in GitHub Desktop.
Save mudkipdev/4b0ce96a2d9bd6205e16c52798546571 to your computer and use it in GitHub Desktop.
A discord.py embed pagination system using buttons.
from discord.ext import commands
import discord
import asyncio
__all__ = ("Paginator",)
class Paginator:
def __init__(
self,
embeds: list[discord.Embed],
*,
index: int = 0,
author: discord.User | None = None
) -> None:
self.embeds = embeds
self.index = index
self.author = author
self.paginated_view = PaginatedView(self.embeds, author=self.author)
async def start(
self,
messageable: discord.abc.Messageable | discord.Interaction,
*args,
**kwargs
) -> None:
kwargs["embed"] = self.embeds[self.index]
kwargs["view"] = self.paginated_view
if isinstance(messageable, discord.Interaction):
await messageable.response.send_message(*args, **kwargs)
elif isinstance(messageable, discord.abc.Messageable):
await messageable.send(*args, **kwargs)
self.paginated_view.set_index(self.index)
class PaginatedView(discord.ui.View):
def __init__(
self, embeds: list[discord.Embed], *, author: discord.User | None = None
) -> None:
super().__init__(timeout=None)
self.embeds = embeds
self.index = 0
self.author = author
@discord.ui.button(custom_id="previous", emoji="◀")
async def previous(
self, interaction: discord.Interaction, button: discord.ui.Button
) -> None:
self.set_index(self.index - 1)
await self.update(interaction)
@discord.ui.button(custom_id="page", emoji="🔢")
async def page(
self, interaction: discord.Interaction, button: discord.ui.Button
) -> None:
if self.author and interaction.user != self.author:
await interaction.response.send_message(
embed=discord.Embed(
description="You do not have permission to interact with this menu.",
color=discord.Color.dark_embed(),
),
ephemeral=True,
)
return
await interaction.response.send_modal(PageModal(self))
@discord.ui.button(custom_id="next", emoji="▶")
async def next(
self, interaction: discord.Interaction, button: discord.ui.Button
) -> None:
self.set_index(self.index + 1)
await self.update(interaction)
async def update(self, interaction: discord.Interaction) -> None:
if self.author and interaction.user != self.author:
await interaction.response.send_message(
embed=discord.Embed(
description="You do not have permission to interact with this menu.",
color=discord.Color.dark_embed(),
),
ephemeral=True,
)
return
await interaction.response.edit_message(
embed=self.embeds[self.index], view=self
)
def set_index(self, index: int) -> None:
if not 0 <= index < len(self.embeds):
return
self.previous.disabled = index == 0
self.next.disabled = index == len(self.embeds) - 1
self.index = index
class PageModal(discord.ui.Modal, title="Skip to Page"):
page = discord.ui.TextInput(label="Page", required=True)
def __init__(self, paginated_view: PaginatedView) -> None:
super().__init__()
self.paginated_view = paginated_view
async def on_submit(self, interaction: discord.Interaction) -> None:
if self.page.value.isdigit():
self.paginated_view.set_index(int(self.page.value) - 1)
await self.paginated_view.update(interaction)
else:
await interaction.response.send_message(
embed=discord.Embed(
description="That page number is invalid.",
color=discord.Color.dark_embed(),
),
ephemeral=True,
)
# Slash Command Example (With Cogs)
@app_commands.command(description="Sends a paginated message.")
async def test(self, interaction: discord.Interaction) -> None:
paginator = Paginator(embeds=[discord.Embed(
description=f"{page + 1}",
color=discord.Color.random()
) for page in range(5)])
await paginator.start(interaction)
# Slash Command Example (Without Cogs)
@bot.tree.command(description="Sends a paginated message.")
async def test(interaction: discord.Interaction) -> None:
paginator = Paginator(embeds=[discord.Embed(
description=f"{page + 1}",
color=discord.Color.random()
) for page in range(5)])
await paginator.start(interaction)
# Message Command Example (With Cogs)
@commands.command(help="Sends a paginated message.")
async def test(self, ctx: commands.Context) -> None:
paginator = Paginator(embeds=[discord.Embed(
description=f"{page + 1}",
color=discord.Color.random()
) for page in range(5)])
await paginator.start(ctx)
# Message Command Example (Without Cogs)
@bot.command(help="Sends a paginated message.")
async def test(ctx: commands.Context) -> None:
paginator = Paginator(embeds=[discord.Embed(
description=f"{page + 1}",
color=discord.Color.random()
) for page in range(5)])
await paginator.start(ctx)
# Hybrid Command Example (With Cogs)
# (coming soon)
# Hybrid Command Example (Without Cogs)
# (coming soon)
@egirl
Copy link

egirl commented Feb 26, 2023

looks good mate 👍

@SpookyO
Copy link

SpookyO commented Feb 26, 2023

Looking great tbh, but there should be a toggle option too, weather the user want to flag other user if the view is not for him or not. But other codes are good! Also there should be handler when isdigit() returns False. And done basically, Oh forgot to mention, this paginator should raise a error if the list of embeds is not 2 or more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment