diff --git a/src/dpy_paginator/dpy_paginator.py b/src/dpy_paginator/dpy_paginator.py index d307c06..f2516b3 100644 --- a/src/dpy_paginator/dpy_paginator.py +++ b/src/dpy_paginator/dpy_paginator.py @@ -29,6 +29,7 @@ config-free: no host config import; everything is passed at construction. from __future__ import annotations import asyncio +import logging from typing import ( Any, Dict, @@ -69,6 +70,8 @@ DEFAULT_EMOJIS = { "cache": "\U0001f5c2\ufe0f", # 🗂️ } +log = logging.getLogger(__name__) + PageT_co = TypeVar("PageT_co", bound=Page, covariant=True) @@ -161,6 +164,10 @@ class ButtonPaginator(Generic[PageT_co], discord.ui.View): super().__init__(timeout=timeout) if not pages: raise ValueError("ButtonPaginator requires at least one page") + if per_page < 1: + # per_page <= 0 would ZeroDivisionError (==0) or yield a negative max_pages + # (<0) at the divmod below; fail loud like the other construction guards + raise ValueError("per_page must be >= 1") self.author_id: Optional[int] = author_id self.delete_message_after: bool = delete_message_after self.mentions_allowed = mentions_allowed or discord.AllowedMentions.all() @@ -427,3 +434,7 @@ class ButtonPaginator(Generic[PageT_co], discord.ui.View): await self.message.delete() except (discord.NotFound, discord.Forbidden): pass + except discord.HTTPException: + # on_timeout runs as a fire-and-forget task; a transient delete failure must + # not surface as an unretrieved-task traceback on a best-effort cleanup + log.warning("paginator on_timeout: failed to delete message", exc_info=True)