dpy-paginator

Table of contents
Built and tested on discord.py 2.3.2
A discord.py utility with no external dependencies that makes paginating embeds easier.
Quick links
Changelog (v1.0.0 -> v1.1.0 and v1.1.1)
- Added the ability to use custom emojis in buttons.
- Code optimizations.
- Code cleanup.
- v1.1.0 -> v1.1.1 contains only readme updates and pyproject.toml changes for metadata.
Some of it's features include:
- Easy to use.
- Supports both ephemeral and non-ephemeral responses.
- Buttons are enabled/disabled automatically depending on the current page number, number of embeds provided or at timeout.
The paginator consists of 4 buttons - ⏪, ◀️, 'Jump To' modal, ▶️, ⏩
- ⏪ - Shows the first embed. Disabled if there are less than 3 embeds or if already on the first embed.
- ◀️ - Shows the previous embed. Disabled if already on the first embed.
- 'Jump To' modal - Triggers a
discord.ui.Modal
that takes you to the page number you input. Disabled if there are less than 4 embeds.
- ▶️ - Shows the next embed. Disabled if already on the last embed.
- ⏩ - Shows the last embed. Disabled if there are less than 3 embeds or if already on the last page.
Installation
pip install dpy-paginator
or
pip install git+https://github.com/onceyt/dpy-paginator.git@v1.1.0
Usage
Basic usage:
import discord
from dpy_paginator import paginate
embed1 = discord.Embed(title = "This is embed#1")
embed2 = discord.Embed(title = "This is embed#2")
output = paginate(embeds = [embed1, embed2])
await Messagable.send(embed = output.embed, view = output.view)
discord.ext.Commands Example:
import discord
from discord.ext import commands
from dpy_paginator import paginate
bot = discord.Bot()
@bot.command()
async def example(ctx: commands.Context):
embed1 = discord.Embed(title = "This is Embed#1")
embed2 = discord.Embed(title = "This is Embed#2")
output = paginate(embeds = [embed1, embed2])
await ctx.send(embed = output.embed, view = output.view)
This command has the following output:

discord.app_commands Example: (ephemeral)
from discord import app_commands
from dpy_paginator import paginate
@app_commands.command(name='example')
async def example_command(interaction: discord.Interaction):
await interaction.response.defer(ephemeral = True, thinking = True)
embed1 = discord.Embed(title = "This is Embed#1")
embed2 = discord.Embed(title = "This is Embed#2")
output = paginate(embeds = [embed1, embed2])
await interaction.followup.send(embed = output.embed, view = output.view)
This command has the following output:

Options and Parameters
Control who can interact: (author_ids: list[int]
param)
You can control which user(s) can interact with the view by passing a author_ids
list.
...
output = paginate(embeds = [embed1, embed2], author_ids = [
When anyone except the specified user(s) try to interact, the paginator ignores that interaction:

Adding a timeout: (timeout: int
param)
By default, the view has a timeout of 90 seconds but this can be changed by passing a timeout
parameter.
...
output = paginate(embeds = [embed1, embed2], timeout = 60)
The buttons get automatically disabled after timeout (except when no button is interacted with)1. You can also use timeout = None
for no timeout.
Example of a timedout view:

In the scenario that no button is interacted with and the view gets timedout, the buttons will not be automatically disabled resulting in the need of an extra step. output.view.timedout
returns a boolean which we can use to check if the view has timedout.
import asyncio
...
timeout = 60
output = paginate(embeds = [embed1, embed2], timeout = timeout)
message = await Messageable.send(embed = output.embed, view = output.view)
await asyncio.sleep(timeout + 0.5)
if output.view.timedout:
await message.edit(view = output.view)
Note that incase of ephemeral responses (or scenarios where the output will be deleted before the timeout), this extra step is not worth it.
Using custom emojis in buttons: (button_emojis: List[discord.Emoji]
param)
You can use custom emojis in the buttons by passing a list of discord.Emoji objects or Unicode emoji strings. The list needs to have exactly 4 elements.
...
output = paginate(embeds = [embed1, embed2, embed3, embed4], button_emojis = ['<:DarkerTan:945570099081920532>', '<:DazzlingRose:945561181467344977>', '<:FluorescentBlue:945561331547914280>', '😮'])
Example of a view using custom emojis:

Note that the package has no error handling for if you pass a non discord.Emoji object or a non Unicode emoji string in the list. This is because explicitly checking for a discord.Emoji object will not accomodate using an Unicode string. Make sure you are passing the right objects or strings otherwise you will be getting the HTTPException: 400 Bad Request (error code: 50035): Invalid Form Body
error.