# 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.4 ``` Direct: ```bash pip install "dpy_paginator @ git+ssh://git@git.rethinkstudios.io/rethink-public/dpy_paginator.git@v0.1.4" ``` Requires `discord.py` (pulled transitively). Drop the `@v0.1.4` suffix from the line above to install the latest unpinned. ## 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 pins a release; drop the `@vX.Y.Z` suffix to install the latest unpinned. Pin deliberately for reproducible installs.