Repository: Pycord-Development/pycord

Issue:
Add `delay`/`delete_after` arguments to various message modification functions
### Summary

Add `delay`/`delete_after` argument to various message modification functions

### What is the feature request for?

The core library

### The Problem

`InteractonMessage.delete()` provides an optional `delay` argument to specify a float for how long to sleep before actually deleting the message.

`InteractionResponse.send_message()` provides an optional `delete_after' argument similar to above, but how long to sleep before deleting the message after sleeping.

However, nothing similar to this exists for `InteractionMessage.edit()`, `InteractionResponse.edit_message()`, `Interaction.edit_original_message()`, and `Interaction.delete_original_message()`.

### The Ideal Solution

I'd love to see a `delete_after` argument added to `InteractionMessage.edit()`, `InteractionResponse.edit_message()`, and `Interaction.edit_original_message()`, and a `delay` argument added to `Interaction.delete_original_message()` (along with returning the `Interaction.delete_original_message` object in `ApplicationContext.delete()`, rather than just calling the function and returning).

### The Current Solution

_No response_

### Additional Context

_No response_


Discussion:

Resolving patch:
diff --git a/discord/interactions.py b/discord/interactions.py
--- a/discord/interactions.py
+++ b/discord/interactions.py
@@ -272,6 +272,7 @@ async def edit_original_message(
         files: List[File] = MISSING,
         view: Optional[View] = MISSING,
         allowed_mentions: Optional[AllowedMentions] = None,
+        delete_after: Optional[float] = None,
     ) -> InteractionMessage:
         """|coro|
 
@@ -303,6 +304,10 @@ async def edit_original_message(
         view: Optional[:class:`~discord.ui.View`]
             The updated view to update this message with. If ``None`` is passed then
             the view is removed.
+        delete_after: Optional[:class:`float`]
+            If provided, the number of seconds to wait in the background
+            before deleting the message we just edited. If the deletion fails,
+            then it is silently ignored.
 
         Raises
         -------
@@ -346,9 +351,13 @@ async def edit_original_message(
         message = InteractionMessage(state=self._state, channel=self.channel, data=data)  # type: ignore
         if view and not view.is_finished():
             self._state.store_view(view, message.id)
+
+        if delete_after is not None:
+            await self.delete_original_message(delay=delete_after)
+
         return message
 
-    async def delete_original_message(self) -> None:
+    async def delete_original_message(self, *, delay: Optional[float] = None) -> None:
         """|coro|
 
         Deletes the original interaction response message.
@@ -356,6 +365,12 @@ async def delete_original_message(self) -> None:
         This is a lower level interface to :meth:`InteractionMessage.delete` in case
         you do not want to fetch the message and save an HTTP request.
 
+        Parameters
+        -----------
+        delay: Optional[:class:`float`]
+            If provided, the number of seconds to wait before deleting the message.
+            The waiting is done in the background and deletion failures are ignored.
+
         Raises
         -------
         HTTPException
@@ -364,12 +379,17 @@ async def delete_original_message(self) -> None:
             Deleted a message that is not yours.
         """
         adapter = async_context.get()
-        await adapter.delete_original_interaction_response(
+        func = adapter.delete_original_interaction_response(
             self.application_id,
             self.token,
             session=self._session,
         )
 
+        if delay is not None:
+            utils.delay_task(delay, func)
+        else:
+            await func
+
 
 class InteractionResponse:
     """Represents a Discord interaction response.
@@ -600,10 +620,7 @@ async def send_message(
 
         self._responded = True
         if delete_after is not None:
-            async def delete():
-                await asyncio.sleep(delete_after)
-                await self._parent.delete_original_message()
-            asyncio.ensure_future(delete(), loop=self._parent._state.loop)
+            await self._parent.delete_original_message(delay=delete_after)
         return self._parent
 
     async def edit_message(
@@ -614,6 +631,7 @@ async def edit_message(
         embeds: List[Embed] = MISSING,
         attachments: List[Attachment] = MISSING,
         view: Optional[View] = MISSING,
+        delete_after: Optional[float] = None
     ) -> None:
         """|coro|
 
@@ -635,6 +653,10 @@ async def edit_message(
         view: Optional[:class:`~discord.ui.View`]
             The updated view to update this message with. If ``None`` is passed then
             the view is removed.
+        delete_after: Optional[:class:`float`]
+            If provided, the number of seconds to wait in the background
+            before deleting the message we just edited. If the deletion fails,
+            then it is silently ignored.
 
         Raises
         -------
@@ -697,6 +719,8 @@ async def edit_message(
             state.store_view(view, message_id)
 
         self._responded = True
+        if delete_after is not None:
+            await self._parent.delete_original_message(delay=delete_after)
 
     async def send_autocomplete_result(
         self,
@@ -789,6 +813,7 @@ async def edit(
         files: List[File] = MISSING,
         view: Optional[View] = MISSING,
         allowed_mentions: Optional[AllowedMentions] = None,
+        delete_after: Optional[float] = None,
     ) -> InteractionMessage:
         """|coro|
 
@@ -814,6 +839,10 @@ async def edit(
         view: Optional[:class:`~discord.ui.View`]
             The updated view to update this message with. If ``None`` is passed then
             the view is removed.
+        delete_after: Optional[:class:`float`]
+            If provided, the number of seconds to wait in the background
+            before deleting the message we just edited. If the deletion fails,
+            then it is silently ignored.
 
         Raises
         -------
@@ -839,6 +868,7 @@ async def edit(
             files=files,
             view=view,
             allowed_mentions=allowed_mentions,
+            delete_after=delete_after
         )
 
     async def delete(self, *, delay: Optional[float] = None) -> None:
@@ -861,16 +891,4 @@ async def delete(self, *, delay: Optional[float] = None) -> None:
         HTTPException
             Deleting the message failed.
         """
-
-        if delay is not None:
-
-            async def inner_call(delay: float = delay):
-                await asyncio.sleep(delay)
-                try:
-                    await self._state._interaction.delete_original_message()
-                except HTTPException:
-                    pass
-
-            asyncio.create_task(inner_call())
-        else:
-            await self._state._interaction.delete_original_message()
+        await self._state._interaction.delete_original_message(delay=delay)
diff --git a/discord/message.py b/discord/message.py
--- a/discord/message.py
+++ b/discord/message.py
@@ -1137,18 +1137,11 @@ async def delete(self, *, delay: Optional[float] = None) -> None:
         HTTPException
             Deleting the message failed.
         """
+        del_func = self._state.http.delete_message(self.channel.id, self.id)
         if delay is not None:
-
-            async def delete(delay: float):
-                await asyncio.sleep(delay)
-                try:
-                    await self._state.http.delete_message(self.channel.id, self.id)
-                except HTTPException:
-                    pass
-
-            asyncio.create_task(delete(delay))
+            utils.delay_task(delay, del_func)
         else:
-            await self._state.http.delete_message(self.channel.id, self.id)
+            await del_func
 
     @overload
     async def edit(
diff --git a/discord/utils.py b/discord/utils.py
--- a/discord/utils.py
+++ b/discord/utils.py
@@ -44,6 +44,7 @@
     Any,
     AsyncIterator,
     Callable,
+    Coroutine,
     Dict,
     ForwardRef,
     Generic,
@@ -222,6 +223,17 @@ def count(self, value: Any) -> int:
         return self.__proxied.count(value)
 
 
+def delay_task(delay: float, func: Coroutine):
+    async def inner_call():
+        await asyncio.sleep(delay)
+        try:
+            await func
+        except HTTPException:
+            pass
+
+    asyncio.create_task(inner_call())
+
+
 @overload
 def parse_time(timestamp: None) -> None:
     ...


Test patch: