dpy_paginator/README.md
2026-06-29 18:07:36 -04:00

143 lines
4.8 KiB
Markdown

# dpy_paginator
Button-navigated paginator for discord.py. A `discord.ui.View` that paginates mixed
content (strings, embeds, files, attachments, or dicts of mixed content with custom
buttons) behind previous / jump / next navigation, with an optional cache button.
## Install
`requirements.txt`:
```
dpy_paginator @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_paginator.git
```
Direct:
```bash
pip install "dpy_paginator @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_paginator.git"
```
Requires `discord.py` (pulled transitively).
## Basic usage
Plain pages — just navigation:
```python
from dpy_paginator import ButtonPaginator
pages = [discord.Embed(title=f"Page {i}") for i in range(5)]
await ButtonPaginator(pages, author_id=ctx.author.id).start(ctx)
```
`start()` accepts an `Interaction` or any `Messageable` (a `Context`, channel, etc.).
## Emojis
Navigation uses plain Unicode by default — no setup, no emoji upload required. Pass
`emojis=` to override with custom application/guild emojis the bot can use:
```python
ButtonPaginator(pages, emojis={
"previous": "<:icon_back:123...>",
"next": "<:icon_next:123...>",
"cache": "<:icon_cache:123...>",
})
```
Unset keys fall back to the Unicode defaults.
## Page types
A page may be a `str`, `discord.Embed`, `discord.File`/`Attachment`, a sequence of
those, or a `dict`. A dict page can carry `content`, `embed`/`embeds`,
`file`/`files`, and a `buttons` list of custom button configs.
## Custom per-page buttons
Each page can declare its own buttons. They render alongside the navigation row and
are rebuilt from the page dict on every render — so mutating a page's button config
and re-rendering updates the button live.
```python
async def callback_reorder(interaction, button, data, paginator):
await interaction.response.send_message(f"Reordering {data['email']}", ephemeral=True)
pages = []
for session in sessions:
pages.append({
"content": session["_id"],
"embed": build_embed(session),
"buttons": [
{
"label": "Reorder",
"style": discord.ButtonStyle.green,
"emoji": some_emoji, # optional, your own emoji
"disabled": False,
"data": {"email": session["_id"]},
"callback": callback_reorder,
}
],
})
paginator = ButtonPaginator(
pages, cache=None, timeout=900, delete_message_after=True,
mentions_allowed=discord.AllowedMentions.none(), ephemeral=True,
page_text="Session {} of {}",
)
await paginator.start(interaction)
```
Button config keys: `label`, `style`, `emoji`, `disabled`, `data` (arbitrary, passed
to the callback), `callback`. The callback signature is
`async def cb(interaction, button, data, paginator)`. With no callback, the button
echoes its `data`.
## Cache button (mention priming)
Discord clients render `<@id>` as a raw id until the user object is cached locally.
The cache button fixes that: pass `cache=[...]` with one entry per page, where each
entry is a string of the user mentions on that page (`"<@111> <@222> <@333>"`).
When clicked, the button posts the page's mentions in a throwaway ephemeral message
(rendering the `<@id>` tags primes the viewer's client — `allowed_mentions` is
`none()` so no actual ping fires), waits `cache_sleep` seconds (default 1.0) for the
client to resolve them, then re-renders the current page — so the mentions now
display as names without the user having to flip pages manually.
```python
pages, cache = [], []
for group in groups:
pages.append(build_embed(group))
cache.append(" ".join(f"<@{uid}>" for uid in group["user_ids"]))
await ButtonPaginator(pages, cache=cache, cache_sleep=1.0).start(ctx)
```
Omit `cache` or pass `None`/`[]` and the button never appears. When set, `cache`
must have one entry per rendered page — a shorter `cache` raises `ValueError` at
construction rather than failing with an `IndexError` mid-navigation.
## Constructor options
- `pages` — sequence of page content
- `cache` — optional per-page cache strings (enables the cache button)
- `author_id` — restrict interaction to this user
- `timeout` — seconds before the view stops (default 180)
- `delete_message_after` — delete the message on timeout
- `per_page` — entries per page (default 1)
- `mentions_allowed``AllowedMentions` for sends (default `.all()`)
- `ephemeral` — send/edit ephemerally
- `page_text` — format string for the jump button label
- `emojis` — override navigation emojis
## Subclassing
Override `format_page(page)` to transform pages before rendering (e.g. wrap raw data
in an embed). It may be sync or async.
## Versioning
Releases are tagged `vX.Y.Z`. The install line above is unpinned and tracks the latest on the default branch; append `@vX.Y.Z` to pin a specific release for reproducible installs.