init: button paginator for discord.py
Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
commit
0a1f4f609f
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
# claude
|
||||
CLAUDE.md
|
||||
|
||||
# python
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
build/
|
||||
dist/
|
||||
.eggs/
|
||||
|
||||
# env
|
||||
.venv/
|
||||
venv/
|
||||
.env
|
||||
141
README.md
Normal file
141
README.md
Normal file
@ -0,0 +1,141 @@
|
||||
# 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@v0.1.0
|
||||
```
|
||||
|
||||
Direct:
|
||||
|
||||
```bash
|
||||
pip install "dpy_paginator @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_paginator.git@v0.1.0"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
|
||||
Tagged `vX.Y.Z`. Pin the tag in `requirements.txt`.
|
||||
Loading…
Reference in New Issue
Block a user