diff --git a/discord/ext/pages/pagination.py b/discord/ext/pages/pagination.py index f92c09d9b4..fcfdba6560 100644 --- a/discord/ext/pages/pagination.py +++ b/discord/ext/pages/pagination.py @@ -126,6 +126,8 @@ class Page: The content of the page. Corresponds to the :class:`discord.Message.content` attribute. embeds: Optional[List[Union[List[:class:`discord.Embed`], :class:`discord.Embed`]]] The embeds of the page. Corresponds to the :class:`discord.Message.embeds` attribute. + files: Optional[List[:class:`discord.File`]] + A list of local files to be shown with the page. custom_view: Optional[:class:`discord.ui.View`] The custom view shown when the page is visible. Overrides the `custom_view` attribute of the main paginator. """ @@ -135,6 +137,7 @@ def __init__( content: Optional[str] = None, embeds: Optional[List[Union[List[discord.Embed], discord.Embed]]] = None, custom_view: Optional[discord.ui.View] = None, + files: Optional[List[discord.File]] = None, **kwargs, ): if content is None and embeds is None: @@ -142,6 +145,7 @@ def __init__( self._content = content self._embeds = embeds or [] self._custom_view = custom_view + self._files = files or [] async def callback(self, interaction: Optional[discord.Interaction] = None): """|coro| @@ -155,6 +159,19 @@ async def callback(self, interaction: Optional[discord.Interaction] = None): """ pass + def update_files(self) -> Optional[List[discord.File]]: + """Updates the files associated with the page by re-uploading them. + Typically used when the page is changed.""" + for file in self._files: + with open(file.fp.name, "rb") as fp: # type: ignore + self._files[self._files.index(file)] = discord.File( + fp, # type: ignore + filename=file.filename, + description=file.description, + spoiler=file.spoiler, + ) + return self._files + @property def content(self) -> Optional[str]: """Gets the content for the page.""" @@ -185,6 +202,16 @@ def custom_view(self, value: Optional[discord.ui.View]): """Assigns a custom view to be shown when the page is displayed.""" self._custom_view = value + @property + def files(self) -> Optional[List[discord.File]]: + """Gets the files associated with the page.""" + return self._files + + @files.setter + def files(self, value: Optional[List[discord.File]]): + """Sets the files associated with the page.""" + self._files = value + class PageGroup: """Creates a group of pages which the user can switch between. @@ -472,7 +499,14 @@ async def on_timeout(self) -> None: if self.disable_on_timeout: for item in self.children: item.disabled = True - await self.message.edit(view=self) + page = self.pages[self.current_page] + page = self.get_page_content(page) + files = page.update_files() + await self.message.edit( + view=self, + files=files or [], + attachments=[], + ) async def disable( self, @@ -561,12 +595,24 @@ async def goto_page(self, page_number: int = 0, *, interaction: Optional[discord if page.custom_view: self.update_custom_view(page.custom_view) + files = page.update_files() + if interaction: - await interaction.response.edit_message(content=page.content, embeds=page.embeds, view=self) + await interaction.response.defer() # needed to force webhook message edit route for files kwarg support + await interaction.followup.edit_message( + message_id=self.message.id, + content=page.content, + embeds=page.embeds, + attachments=[], + files=files or [], + view=self, + ) else: await self.message.edit( content=page.content, embeds=page.embeds, + attachments=[], + files=files or [], view=self, ) if self.trigger_on_display: @@ -729,16 +775,22 @@ def get_page_content(page: Union[Page, str, discord.Embed, List[discord.Embed]]) if isinstance(page, Page): return page elif isinstance(page, str): - return Page(content=page, embeds=[]) + return Page(content=page, embeds=[], files=[]) elif isinstance(page, discord.Embed): - return Page(content=None, embeds=[page]) + return Page(content=None, embeds=[page], files=[]) + elif isinstance(page, discord.File): + return Page(content=None, embeds=[], files=[page]) elif isinstance(page, List): if all(isinstance(x, discord.Embed) for x in page): - return Page(content=None, embeds=page) + return Page(content=None, embeds=page, files=[]) + if all(isinstance(x, discord.File) for x in page): + return Page(content=None, embeds=[], files=page) else: - raise TypeError("All list items must be embeds.") + raise TypeError("All list items must be embeds or files.") else: - raise TypeError("Page content must be a Page object, string, an embed, or a list of embeds.") + raise TypeError( + "Page content must be a Page object, string, an embed, a list of embeds, a file, or a list of files." + ) async def page_action(self, interaction: Optional[discord.Interaction] = None) -> None: """Triggers the callback associated with the current page, if any. @@ -832,6 +884,7 @@ async def send( self.message = await ctx.send( content=page_content.content, embeds=page_content.embeds, + files=page_content.files, view=self, reference=reference, allowed_mentions=allowed_mentions, @@ -891,10 +944,13 @@ async def edit( self.update_custom_view(page_content.custom_view) self.user = message.author + try: self.message = await message.edit( content=page_content.content, embeds=page_content.embeds, + files=page_content.files, + attachments=[], view=self, suppress=suppress, allowed_mentions=allowed_mentions, @@ -965,12 +1021,14 @@ async def respond( msg = await target.send( content=page_content.content, embeds=page_content.embeds, + files=page_content.files, view=self, ) elif interaction.response.is_done(): msg = await interaction.followup.send( content=page_content.content, embeds=page_content.embeds, + files=page_content.files, view=self, ephemeral=ephemeral, ) @@ -981,6 +1039,7 @@ async def respond( msg = await interaction.response.send_message( content=page_content.content, embeds=page_content.embeds, + files=page_content.files, view=self, ephemeral=ephemeral, ) @@ -992,18 +1051,21 @@ async def respond( msg = await ctx.send( content=page_content.content, embeds=page_content.embeds, + files=page_content.files, view=self, ) else: msg = await ctx.respond( content=page_content.content, embeds=page_content.embeds, + files=page_content.files, view=self, ) if isinstance(msg, (discord.Message, discord.WebhookMessage)): self.message = msg elif isinstance(msg, discord.Interaction): self.message = await msg.original_message() + return self.message