fix(core): Handle rendering errors.
This commit is contained in:
2
src/gui
2
src/gui
Submodule src/gui updated: b781f7f9f2...ba9ace6ced
@@ -12,6 +12,7 @@ from aiohttp import ClientSession
|
||||
|
||||
from data import Database
|
||||
from utils.lib import tabulate
|
||||
from gui.errors import RenderingException
|
||||
|
||||
from .config import Conf
|
||||
from .logger import logging_context, log_context, log_action_stack, log_wrap, set_logging_context
|
||||
@@ -204,13 +205,23 @@ class LionBot(Bot):
|
||||
pass
|
||||
except asyncio.TimeoutError:
|
||||
pass
|
||||
except RenderingException as e:
|
||||
logger.info(f"Command failed due to RenderingException: {repr(e)}")
|
||||
embed = self.tree.rendersplat(e)
|
||||
try:
|
||||
await ctx.error_reply(embed=embed)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.exception(
|
||||
f"Caught an unknown CommandInvokeError while executing: {cmd_str}",
|
||||
extra={'action': 'BotError', 'with_ctx': True}
|
||||
)
|
||||
|
||||
error_embed = discord.Embed(title="Something went wrong!")
|
||||
error_embed = discord.Embed(
|
||||
title="Something went wrong!",
|
||||
colour=discord.Colour.dark_red()
|
||||
)
|
||||
error_embed.description = (
|
||||
"An unexpected error occurred while processing your command!\n"
|
||||
"Our development team has been notified, and the issue will be addressed soon.\n"
|
||||
@@ -246,7 +257,7 @@ class LionBot(Bot):
|
||||
|
||||
try:
|
||||
await ctx.error_reply(embed=error_embed)
|
||||
except Exception:
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
finally:
|
||||
exception.original = HandledException(exception.original)
|
||||
|
||||
@@ -8,9 +8,11 @@ from discord.enums import InteractionType
|
||||
from discord.app_commands.namespace import Namespace
|
||||
|
||||
from utils.lib import tabulate
|
||||
from gui.errors import RenderingException
|
||||
|
||||
from .logger import logging_context, set_logging_context, log_wrap, log_action_stack
|
||||
from .errors import SafeCancellation
|
||||
from .config import conf
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -29,17 +31,36 @@ class LionTree(CommandTree):
|
||||
except SafeCancellation:
|
||||
# Assume this has already been handled
|
||||
pass
|
||||
except RenderingException as e:
|
||||
logger.info(f"Tree interaction failed due to rendering exception: {repr(e)}")
|
||||
embed = self.rendersplat(e)
|
||||
await self.error_reply(interaction, embed)
|
||||
except Exception:
|
||||
logger.exception(f"Unhandled exception in interaction: {interaction}", extra={'action': 'TreeError'})
|
||||
if not interaction.is_expired():
|
||||
splat = self.bugsplat(interaction, error)
|
||||
try:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(embed=splat, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(embed=splat, ephemeral=True)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
embed = self.bugsplat(interaction, error)
|
||||
await self.error_reply(interaction, embed)
|
||||
|
||||
async def error_reply(self, interaction, embed):
|
||||
if not interaction.is_expired():
|
||||
try:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(embed=embed, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(embed=embed, ephemeral=True)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
|
||||
def rendersplat(self, e: RenderingException):
|
||||
embed = discord.Embed(
|
||||
title="Resource Currently Unavailable!",
|
||||
description=(
|
||||
"Sorry, the graphics service is currently unavailable!\n"
|
||||
"Please try again in a few minutes.\n"
|
||||
"If the error persists, please contact our [support team]({link})"
|
||||
).format(link=conf.bot.support_guild),
|
||||
colour=discord.Colour.dark_red()
|
||||
)
|
||||
return embed
|
||||
|
||||
def bugsplat(self, interaction, e):
|
||||
error_embed = discord.Embed(title="Something went wrong!", colour=discord.Colour.red())
|
||||
|
||||
@@ -386,8 +386,9 @@ class TimerCog(LionCog):
|
||||
)
|
||||
else:
|
||||
# Display the timer status ephemerally
|
||||
await ctx.interaction.response.defer(thinking=True, ephemeral=True)
|
||||
status = await timer.current_status(with_notify=False, with_warnings=False)
|
||||
await ctx.reply(**status.send_args, ephemeral=True)
|
||||
await ctx.interaction.edit_original_response(**status.edit_args)
|
||||
|
||||
if error is not None:
|
||||
await ctx.reply(embed=error, ephemeral=True)
|
||||
|
||||
@@ -12,6 +12,7 @@ from utils.lib import MessageArgs, utc_now, replace_multiple
|
||||
from core.lion_guild import LionGuild
|
||||
from core.data import CoreData
|
||||
from babel.translator import ctx_locale
|
||||
from gui.errors import RenderingException
|
||||
|
||||
from . import babel, logger
|
||||
from .data import TimerData
|
||||
@@ -83,6 +84,7 @@ class Timer:
|
||||
self.destroyed = False
|
||||
|
||||
def __repr__(self):
|
||||
# TODO: Add lock status and current state and stage
|
||||
return (
|
||||
"<Timer "
|
||||
f"channelid={self.data.channelid} "
|
||||
@@ -560,19 +562,20 @@ class Timer:
|
||||
"Timer stopped! Press `Start` to restart the timer."
|
||||
)).format(channel=f"<#{self.data.channelid}>")
|
||||
|
||||
card = await get_timer_card(self.bot, self, stage)
|
||||
await card.render()
|
||||
|
||||
if (ui := self.status_view) is None:
|
||||
ui = self.status_view = TimerStatusUI(self.bot, self, self.channel)
|
||||
|
||||
await ui.refresh()
|
||||
|
||||
return MessageArgs(
|
||||
content=content,
|
||||
file=card.as_file(f"pomodoro_{self.data.channelid}.png"),
|
||||
view=ui
|
||||
)
|
||||
card = await get_timer_card(self.bot, self, stage)
|
||||
try:
|
||||
await card.render()
|
||||
file = card.as_file(f"pomodoro_{self.data.channelid}.png")
|
||||
args = MessageArgs(content=content, file=file, view=ui)
|
||||
except RenderingException:
|
||||
args = MessageArgs(content=content, view=ui)
|
||||
|
||||
return args
|
||||
|
||||
@log_wrap(action='Send Timer Status')
|
||||
async def send_status(self, delete_last=True, **kwargs):
|
||||
@@ -785,8 +788,8 @@ class Timer:
|
||||
to_next_stage = (current.end - utc_now()).total_seconds()
|
||||
|
||||
# TODO: Consider request rate and load
|
||||
if to_next_stage > 1 * 60 - drift:
|
||||
time_to_sleep = 1 * 60
|
||||
if to_next_stage > 5 * 60 - drift:
|
||||
time_to_sleep = 5 * 60
|
||||
else:
|
||||
time_to_sleep = to_next_stage
|
||||
|
||||
|
||||
@@ -299,42 +299,42 @@ class LeaderboardUI(StatsUI):
|
||||
|
||||
@button(label="This Season", style=ButtonStyle.grey)
|
||||
async def season_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.SEASON
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="Today", style=ButtonStyle.grey)
|
||||
async def day_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.DAY
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="This Week", style=ButtonStyle.grey)
|
||||
async def week_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.WEEK
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="This Month", style=ButtonStyle.grey)
|
||||
async def month_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.MONTH
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(label="All Time", style=ButtonStyle.grey)
|
||||
async def alltime_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.current_period = LBPeriod.ALLTIME
|
||||
self.focused = True
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@button(emoji=conf.emojis.backward, style=ButtonStyle.grey)
|
||||
async def prev_button(self, press: discord.Interaction, pressed: Button):
|
||||
await press.response.defer(thinking=True)
|
||||
await press.response.defer(thinking=True, ephemeral=True)
|
||||
self.pagen -= 1
|
||||
self.focused = False
|
||||
await self.refresh(thinking=press)
|
||||
|
||||
@@ -10,6 +10,8 @@ from discord.ui import Modal, View, Item
|
||||
from meta.logger import log_action_stack, logging_context
|
||||
from meta.errors import SafeCancellation
|
||||
|
||||
from gui.errors import RenderingException
|
||||
|
||||
from . import logger
|
||||
from ..lib import MessageArgs, error_embed
|
||||
|
||||
@@ -228,6 +230,12 @@ class LeoUI(View):
|
||||
f"Caught a safe cancellation from LeoUI: {e.details}",
|
||||
extra={'action': 'Cancel'}
|
||||
)
|
||||
except RenderingException as e:
|
||||
logger.info(
|
||||
f"UI interaction failed due to rendering exception: {repr(e)}"
|
||||
)
|
||||
embed = interaction.client.tree.rendersplat(e)
|
||||
await interaction.client.tree.error_reply(interaction, embed)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
f"Unhandled interaction exception occurred in item {item!r} of LeoUI {self!r} from interaction: "
|
||||
@@ -235,15 +243,8 @@ class LeoUI(View):
|
||||
extra={'with_ctx': True, 'action': 'UIError'}
|
||||
)
|
||||
# Explicitly handle the bugsplat ourselves
|
||||
if not interaction.is_expired():
|
||||
splat = interaction.client.tree.bugsplat(interaction, error)
|
||||
try:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(embed=splat, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(embed=splat, ephemeral=True)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
splat = interaction.client.tree.bugsplat(interaction, error)
|
||||
await interaction.client.tree.error_reply(interaction, splat)
|
||||
|
||||
|
||||
class MessageUI(LeoUI):
|
||||
@@ -475,21 +476,20 @@ class LeoModal(Modal):
|
||||
"""
|
||||
try:
|
||||
raise error
|
||||
except RenderingException as e:
|
||||
logger.info(
|
||||
f"Modal submit failed due to rendering exception: {repr(e)}"
|
||||
)
|
||||
embed = interaction.client.tree.rendersplat(e)
|
||||
await interaction.client.tree.error_reply(interaction, embed)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
f"Unhandled interaction exception occurred in {self!r}. Interaction: {interaction.data}",
|
||||
extra={'with_ctx': True, 'action': 'ModalError'}
|
||||
)
|
||||
# Explicitly handle the bugsplat ourselves
|
||||
if not interaction.is_expired():
|
||||
splat = interaction.client.tree.bugsplat(interaction, error)
|
||||
try:
|
||||
if interaction.response.is_done():
|
||||
await interaction.followup.send(embed=splat, ephemeral=True)
|
||||
else:
|
||||
await interaction.response.send_message(embed=splat, ephemeral=True)
|
||||
except discord.HTTPException:
|
||||
pass
|
||||
splat = interaction.client.tree.bugsplat(interaction, error)
|
||||
await interaction.client.tree.error_reply(interaction, splat)
|
||||
|
||||
|
||||
def error_handler_for(exc):
|
||||
|
||||
Reference in New Issue
Block a user