Skip to content

Commit 5c17f4d

Browse files
committed
Provide sane UI defaults + error handling
1 parent a81762d commit 5c17f4d

File tree

9 files changed

+98
-35
lines changed

9 files changed

+98
-35
lines changed

bot/cogs/dev_tools.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import discord
44
from cogs import EXTENSIONS
5+
from discord import app_commands
56
from discord.ext import commands
67
from discord.ext.commands import Context, Greedy
78

@@ -19,7 +20,7 @@ def __init__(self, bot: Rodhaj):
1920
# https://about.abstractumbra.dev/discord.py/2023/01/29/sync-command-example.html
2021
@commands.guild_only()
2122
@commands.is_owner()
22-
@commands.command(name="sync", hidden=True)
23+
@commands.command(name="sync")
2324
async def sync(
2425
self,
2526
ctx: Context,
@@ -65,7 +66,7 @@ async def sync(
6566

6667
@commands.guild_only()
6768
@commands.is_owner()
68-
@commands.command(name="reload-all", hidden=True)
69+
@commands.command(name="reload-all")
6970
async def reload_all(self, ctx: commands.Context) -> None:
7071
"""Reloads all cogs. Used in production to not produce any downtime"""
7172
if not hasattr(self.bot, "uptime"):

bot/launcher.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
from rodhaj import Rodhaj
1212

1313
# Only used for Windows development
14-
if os.name == "nt":
15-
asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
16-
else:
17-
try:
18-
import uvloop
19-
20-
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
21-
except ImportError:
22-
pass
14+
# if os.name == "nt":
15+
# asyncio.set_event_loop_policy(asyncio.WindowsProactorEventLoopPolicy())
16+
# else:
17+
# try:
18+
# import uvloop
19+
20+
# asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
21+
# except ImportError:
22+
# pass
2323

2424
load_dotenv()
2525

bot/libs/utils/__init__.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
from .embeds import Embed as Embed
2-
from .embeds import ErrorEmbed as ErrorEmbed
1+
from .embeds import Embed as Embed, ErrorEmbed as ErrorEmbed
32
from .logger import RodhajLogger as RodhajLogger
3+
from .modals import RoboModal as RoboModal
44
from .time import human_timedelta as human_timedelta
55
from .tree import RodhajCommandTree as RodhajCommandTree
66
from .views import RoboView as RoboView

bot/libs/utils/errors.py

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import traceback
2+
3+
import discord
4+
5+
from .embeds import ErrorEmbed
6+
7+
8+
def produce_error_embed(error: Exception) -> ErrorEmbed:
9+
error_traceback = "\n".join(traceback.format_exception_only(type(error), error))
10+
embed = ErrorEmbed()
11+
desc = f"""
12+
Uh oh! It seems like there was an issue. Ask the devs for help.
13+
14+
**Error**:
15+
```{error_traceback}```
16+
"""
17+
embed.description = desc
18+
embed.set_footer(text="Happened At")
19+
embed.timestamp = discord.utils.utcnow()
20+
return embed

bot/libs/utils/modals.py

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import discord
2+
3+
from .errors import produce_error_embed
4+
5+
NO_CONTROL_MSG = "This modal cannot be controlled by you, sorry!"
6+
7+
8+
class RoboModal(discord.ui.Modal):
9+
"""Subclassed `discord.ui.Modal` that includes sane default configs"""
10+
11+
def __init__(self, interaction: discord.Interaction, *args, **kwargs):
12+
super().__init__(*args, **kwargs)
13+
self.interaction = interaction
14+
15+
async def interaction_check(self, interaction: discord.Interaction, /) -> bool:
16+
if interaction.user and interaction.user.id in (
17+
self.interaction.client.application.owner.id, # type: ignore
18+
self.interaction.user.id,
19+
):
20+
return True
21+
await interaction.response.send_message(NO_CONTROL_MSG, ephemeral=True)
22+
return False
23+
24+
async def on_error(
25+
self, interaction: discord.Interaction, error: Exception, /
26+
) -> None:
27+
await interaction.response.send_message(
28+
embed=produce_error_embed(error), ephemeral=True
29+
)
30+
self.stop()

bot/libs/utils/pages/__init__.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
from .paginator import HajPages as HajPages
22
from .simple_pages import SimplePages as SimplePages
3-
from .sources import EmbedListSource as EmbedListSource
4-
from .sources import SimplePageSource as SimplePageSource
3+
from .sources import (
4+
EmbedListSource as EmbedListSource,
5+
SimplePageSource as SimplePageSource,
6+
)

bot/libs/utils/tree.py

+4-18
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,14 @@
1-
import traceback
2-
31
import discord
42
from discord import app_commands
53
from discord.utils import utcnow
64
from libs.utils import ErrorEmbed
75

6+
from .errors import produce_error_embed
87

9-
class RodhajCommandTree(app_commands.CommandTree):
10-
def build_error_embed(self, error: app_commands.AppCommandError) -> ErrorEmbed:
11-
error_traceback = "\n".join(traceback.format_exception_only(type(error), error))
12-
embed = ErrorEmbed()
13-
embed.description = f"""
14-
Uh oh! It seems like the command ran into an issue!
15-
16-
**Error**:
17-
```
18-
{error_traceback}
19-
```
20-
"""
21-
embed.set_footer(text="Happened At")
22-
embed.timestamp = utcnow()
23-
return embed
248

9+
# Later on if we needed global interaction checks, we can do it here
10+
class RodhajCommandTree(app_commands.CommandTree):
2511
async def on_error(
2612
self, interaction: discord.Interaction, error: app_commands.AppCommandError
2713
) -> None:
28-
await interaction.response.send_message(embed=self.build_error_embed(error))
14+
await interaction.response.send_message(embed=produce_error_embed(error))

bot/libs/utils/views.py

+23-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1+
from typing import Any
2+
13
import discord
24

3-
NO_CONTROL_MSG = "This menu cannot be controlled by you, sorry!"
5+
from .errors import produce_error_embed
6+
7+
NO_CONTROL_MSG = "This view cannot be controlled by you, sorry!"
48

59

6-
# TODO: Include the view that Soheab created
710
class RoboView(discord.ui.View):
11+
"""Subclassed `discord.ui.View` that includes sane default configs"""
12+
813
def __init__(self, interaction: discord.Interaction):
914
super().__init__()
1015
self.interaction = interaction
@@ -17,3 +22,19 @@ async def interaction_check(self, interaction: discord.Interaction, /) -> bool:
1722
return True
1823
await interaction.response.send_message(NO_CONTROL_MSG, ephemeral=True)
1924
return False
25+
26+
async def on_error(
27+
self,
28+
interaction: discord.Interaction,
29+
error: Exception,
30+
item: discord.ui.Item[Any],
31+
/,
32+
) -> None:
33+
await interaction.response.send_message(
34+
embed=produce_error_embed(error), ephemeral=True
35+
)
36+
self.stop()
37+
38+
async def on_timeout(self) -> None:
39+
if self.interaction.response.is_done():
40+
await self.interaction.edit_original_response(view=None)

pyproject.toml

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ jishaku = "^2.5.2"
2828

2929
[tool.isort]
3030
profile = 'black'
31+
combine_as_imports = true
32+
combine_star = true
33+
line_length = 80
3134

3235
[tool.pyright]
3336
include = ["bot/**"]

0 commit comments

Comments
 (0)