Repository: Cog-Creators/Red-DiscordBot

Issue:
[Audio] Local play menu raises NotFound with timeout
### What Red version are you using?

3.5.3

### Cog name

Audio

### Command name

local play

### What did you expect to happen?

For the menus to be handled appropriately.

### What actually happened?

```
[17:35:52] ERROR [asyncio] Task exception was never retrieved
future: <Task finished name='discord-ui-view-timeout-1e3e49d1f60611a8af46ca83d34360b7' coro=<SimpleMenu.on_timeout() done, defined at /Users/aikaterna/red38/lib/python3.8/site-packages/redbot/core/utils/views.py:195> exception=NotFound('404 Not Found (error code: 10008): Unknown Message')>
Traceback (most recent call last)
/Users/aikaterna/red38/lib/python3.8/site-packages/redbot/core/utils/views.py:203 in on_timeout
❱ 203 await self.message.edit(view=None)
/Users/aikaterna/red38/lib/python3.8/site-packages/discord/message.py:2275 in edit
❱ 2275 data = await self._state.http.edit_message(self.channel.id, self.id,
params=params)
/Users/aikaterna/red38/lib/python3.8/site-packages/discord/http.py:741 in request
❱ 741 raise NotFound(response, data)

NotFound: 404 Not Found (error code: 10008): Unknown Message
[17:35:53] ERROR [discord.ui.view] Ignoring exception in view <SimpleMenu timeout=30.0 children=8> for item <_GenericButton style=<ButtonStyle.secondary: 2> url=None disabled=False label=None emoji=<PartialEmoji animated=False name='1⃣' id=None> row=None>
Traceback (most recent call last)
/Users/aikaterna/red38/lib/python3.8/site-packages/discord/ui/view.py:427 in _scheduled_task
❱ 427 await item.callback(interaction)
/Users/aikaterna/red38/lib/python3.8/site-packages/redbot/core/utils/menus.py:55 in callback
❱ 55 await interaction.response.defer()
/Users/aikaterna/red38/lib/python3.8/site-packages/discord/interactions.py:661 in defer
❱ 661 await adapter.create_interaction_response(
/Users/aikaterna/red38/lib/python3.8/site-packages/discord/webhook/async_.py:219 in request
❱ 219 raise NotFound(response, data)
NotFound: 404 Not Found (error code: 10062): Unknown interaction
```

### How can we reproduce this error?

Have a localtracks setup with a named folder with tracks inside of it
Use [p]local play
Push a number button to select a folder, or use the close button for a slightly different traceback
It displays Files Found with another button menu display for tracks inside that folder
Wait for 30 seconds and don't select a track
Receive traceback

### Anything else?

_No response_


Discussion:

Resolving patch:
diff --git a/redbot/core/utils/menus.py b/redbot/core/utils/menus.py
--- a/redbot/core/utils/menus.py
+++ b/redbot/core/utils/menus.py
@@ -37,6 +37,7 @@ def __init__(self, emoji: discord.PartialEmoji, func: _ControlCallable):
         self.func = func
 
     async def callback(self, interaction: discord.Interaction):
+        await interaction.response.defer()
         ctx = self.view.ctx
         pages = self.view.source.entries
         controls = None
@@ -52,7 +53,6 @@ async def callback(self, interaction: discord.Interaction):
             await self.func(ctx, pages, controls, message, page, timeout, emoji)
         except Exception:
             pass
-        await interaction.response.defer()
 
 
 async def menu(
diff --git a/redbot/core/utils/views.py b/redbot/core/utils/views.py
--- a/redbot/core/utils/views.py
+++ b/redbot/core/utils/views.py
@@ -193,14 +193,18 @@ def source(self):
         return self._source
 
     async def on_timeout(self):
-        if self.delete_after_timeout and not self.message.flags.ephemeral:
-            await self.message.delete()
-        elif self.disable_after_timeout:
-            for child in self.children:
-                child.disabled = True
-            await self.message.edit(view=self)
-        else:
-            await self.message.edit(view=None)
+        try:
+            if self.delete_after_timeout and not self.message.flags.ephemeral:
+                await self.message.delete()
+            elif self.disable_after_timeout:
+                for child in self.children:
+                    child.disabled = True
+                await self.message.edit(view=self)
+            else:
+                await self.message.edit(view=None)
+        except discord.HTTPException:
+            # message could no longer be there or we may not be able to edit/delete it anymore
+            pass
 
     def _get_select_menu(self):
         # handles modifying the select menu if more than 25 pages are provided
@@ -523,9 +527,14 @@ async def on_timeout(self):
         if self.disable_buttons:
             self.confirm_button.disabled = True
             self.dismiss_button.disabled = True
-            await self.message.edit(view=self)
+            view = self
         else:
-            await self.message.edit(view=None)
+            view = None
+        try:
+            await self.message.edit(view=view)
+        except discord.HTTPException:
+            # message could no longer be there or we may not be able to edit it anymore
+            pass
 
     @discord.ui.button(label=_("Yes"), style=discord.ButtonStyle.green)
     async def confirm_button(self, interaction: discord.Interaction, button: discord.ui.Button):


Test patch: