From e5c662ba2001d0991fef89bb1ff46df1d94a7c54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20Hells=C3=A9n?= Date: Sat, 5 Jul 2025 04:00:57 +0200 Subject: [PATCH] Add job id to logger.error when failing to format the __getstate__ dictionary for Discord markdown --- discord_reminder_bot/main.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/discord_reminder_bot/main.py b/discord_reminder_bot/main.py index c85024f..7cc8f69 100644 --- a/discord_reminder_bot/main.py +++ b/discord_reminder_bot/main.py @@ -52,21 +52,20 @@ sentry_sdk.init( ) -def generate_state(state: dict[str, Any]) -> str: +def generate_state(state: dict[str, Any], job: Job) -> str: """Format the __getstate__ dictionary for Discord markdown. Args: state (dict): The __getstate__ dictionary. + job (Job): The APScheduler job. Returns: str: The formatted string. """ if not state: + logger.error(f"No state found for {job.id}") return "No state found.\n" - # discord.app_commands.errors.CommandInvokeError: Command 'remove' raised an exception: TypeError: Object of type IntervalTrigger is not JSON serializable - - # Convert the IntervalTrigger to a string representation for key, value in state.items(): if isinstance(value, IntervalTrigger): state[key] = "IntervalTrigger" @@ -88,16 +87,17 @@ def generate_state(state: dict[str, Any]) -> str: return msg -def generate_markdown_state(state: dict[str, Any]) -> str: +def generate_markdown_state(state: dict[str, Any], job: Job) -> str: """Format the __getstate__ dictionary for Discord markdown. Args: state (dict): The __getstate__ dictionary. + job (Job): The APScheduler job. Returns: str: The formatted string. """ - msg: str = generate_state(state) + msg: str = generate_state(state=state, job=job) return "```json\n" + msg + "\n```" @@ -263,7 +263,7 @@ class RemindBotClient(discord.Client): Args: intents: The intents to use. """ - super().__init__(intents=intents) + super().__init__(intents=intents, max_messages=None) self.tree = discord.app_commands.CommandTree(self) async def on_error(self, event_method: str, *args: list[Any], **kwargs: dict[str, Any]) -> None: @@ -272,7 +272,12 @@ class RemindBotClient(discord.Client): sentry_sdk.capture_exception() # TODO(TheLovinator): Add more context to the error # noqa: TD003 async def on_ready(self) -> None: - """Log when the bot is ready.""" + """Called when the client is done preparing the data received from Discord. Usually after login is successful and the Client.guilds and co. are filled up. + + Warning: + This function is not guaranteed to be the first event called. Likewise, this function is not guaranteed to only be called once. + discord.py implements reconnection logic and thus will end up calling this event whenever a RESUME request fails. + """ logger_format = ( "{time:YYYY-MM-DD HH:mm:ss.SSS} | {extra[session_id]} | " "{name}:{function}:{line} - " @@ -301,10 +306,11 @@ class RemindBotClient(discord.Client): except (AttributeError, LookupError): logger.exception("Failed to loop through jobs") - await self.tree.sync(guild=None) + await self.tree.sync() + logger.info("Command tree synced.") if not scheduler.running: - logger.info("Starting the scheduler...") + logger.info("Starting scheduler.") scheduler.start() else: logger.error("Scheduler is already running.") @@ -336,7 +342,7 @@ def format_job_for_ui(job: Job) -> str: if channel and isinstance(channel, discord.abc.GuildChannel | discord.Thread): msg += f"Channel: #{channel.name}\n" - msg += f"\nData:\n{generate_state(job.__getstate__())}\n" + msg += f"\nData:\n{generate_state(job.__getstate__(), job)}\n" logger.debug(f"Formatted job for UI: {msg}") return msg @@ -1245,7 +1251,7 @@ class RemindGroup(discord.app_commands.Group): scheduler.remove_job(job_id) logger.info(f"Removed job {job_id}. {job.__getstate__()}") await interaction.followup.send( - content=f"Reminder with ID {job_id} removed successfully.\n{generate_markdown_state(job.__getstate__())}", + content=f"Reminder with ID {job_id} removed successfully.\n{generate_markdown_state(job.__getstate__(), job=job)}", ) except JobLookupError as e: logger.exception(f"Failed to remove job {job_id}")