Add job event listener for missed jobs and errors; integrate Sentry for error tracking
This commit is contained in:
@ -11,6 +11,8 @@ from typing import TYPE_CHECKING, Any
|
||||
|
||||
import discord
|
||||
import sentry_sdk
|
||||
from apscheduler import events
|
||||
from apscheduler.events import EVENT_JOB_ERROR, EVENT_JOB_MISSED, JobExecutionEvent
|
||||
from apscheduler.job import Job
|
||||
from discord.abc import PrivateChannel
|
||||
from discord_webhook import DiscordWebhook
|
||||
@ -41,6 +43,27 @@ scheduler: settings.AsyncIOScheduler = get_scheduler()
|
||||
msg_to_cleanup: list[discord.InteractionMessage] = []
|
||||
|
||||
|
||||
def my_listener(event: JobExecutionEvent) -> None:
|
||||
"""Listener for job events.
|
||||
|
||||
Args:
|
||||
event: The event that occurred.
|
||||
"""
|
||||
if event.code == events.EVENT_JOB_MISSED:
|
||||
scheduled_time: str = event.scheduled_run_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
msg: str = f"Job {event.job_id} was missed! Was scheduled at {scheduled_time}"
|
||||
send_webhook(message=msg)
|
||||
|
||||
if event.exception:
|
||||
with sentry_sdk.push_scope() as scope:
|
||||
scope.set_extra("job_id", event.job_id)
|
||||
scope.set_extra("scheduled_run_time", event.scheduled_run_time.isoformat() if event.scheduled_run_time else "None")
|
||||
scope.set_extra("event_code", event.code)
|
||||
sentry_sdk.capture_exception(event.exception)
|
||||
|
||||
send_webhook(f"discord-reminder-bot failed to send message to Discord\n{event}")
|
||||
|
||||
|
||||
class RemindBotClient(discord.Client):
|
||||
"""Custom client class for the bot."""
|
||||
|
||||
@ -124,6 +147,7 @@ class RemindBotClient(discord.Client):
|
||||
async def setup_hook(self) -> None:
|
||||
"""Setup the bot."""
|
||||
scheduler.start()
|
||||
scheduler.add_listener(my_listener, EVENT_JOB_MISSED | EVENT_JOB_ERROR)
|
||||
jobs: list[Job] = scheduler.get_jobs()
|
||||
if not jobs:
|
||||
logger.info("No jobs available.")
|
||||
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import sentry_sdk
|
||||
from apscheduler.triggers.date import DateTrigger
|
||||
from loguru import logger
|
||||
|
||||
@ -24,9 +25,17 @@ def calculate(job: Job) -> str | None:
|
||||
|
||||
# Check if the job is paused
|
||||
if trigger_time is None:
|
||||
logger.error(f"Couldn't calculate time for job: {job.id}")
|
||||
logger.error(f"State: {job.__getstate__() if hasattr(job, '__getstate__') else 'No state'}")
|
||||
return None
|
||||
with sentry_sdk.push_scope() as scope:
|
||||
scope.set_tag("job_id", job.id)
|
||||
scope.set_extra("job_state", job.__getstate__() if hasattr(job, "__getstate__") else "No state")
|
||||
sentry_sdk.capture_exception(Exception("Couldn't calculate time for job"))
|
||||
|
||||
msg: str = f"Couldn't calculate time for job: {job.id}"
|
||||
if hasattr(job, "__getstate__"):
|
||||
msg += f"State: {job.__getstate__()}"
|
||||
|
||||
logger.error(msg)
|
||||
return None
|
||||
|
||||
return f"<t:{int(trigger_time.timestamp())}:R>"
|
||||
|
||||
|
@ -11,7 +11,7 @@ from apscheduler.triggers.interval import IntervalTrigger
|
||||
from discord.ui import Button, Select
|
||||
from loguru import logger
|
||||
|
||||
from discord_reminder_bot.misc import DateTrigger, calc_time, calculate
|
||||
from discord_reminder_bot.misc import calc_time, calculate
|
||||
from discord_reminder_bot.parser import parse_time
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@ -309,33 +309,33 @@ class JobManagementView(discord.ui.View):
|
||||
|
||||
job_msg: str | int = job_kwargs.get("message", "No message found")
|
||||
msg: str = f"**Job '{job_msg}' has been deleted.**\n"
|
||||
msg += f"**Job ID**: {self.job.id}\n"
|
||||
msg += f"**Job ID**: {self.job.id}"
|
||||
|
||||
# The time the job was supposed to run
|
||||
if hasattr(self.job, "next_run_time"):
|
||||
if self.job.next_run_time:
|
||||
msg += f"**Next run time**: {self.job.next_run_time} ({calculate(self.job)})\n"
|
||||
msg += f"\n**Next run time**: {self.job.next_run_time} ({calculate(self.job)})"
|
||||
else:
|
||||
msg += "**Next run time**: Paused\n"
|
||||
msg += f"**Trigger**: {self.job.trigger}\n"
|
||||
msg += "\n**Next run time**: Paused"
|
||||
msg += f"\n**Trigger**: {self.job.trigger}"
|
||||
else:
|
||||
msg += "**Next run time**: Pending\n"
|
||||
msg += "\n**Next run time**: Pending\n"
|
||||
|
||||
# The Discord user who created the job
|
||||
if job_kwargs.get("author_id"):
|
||||
msg += f"**Created by**: <@{job_kwargs.get('author_id')}>\n"
|
||||
msg += f"\n**Created by**: <@{job_kwargs.get('author_id')}>"
|
||||
|
||||
# The Discord channel to send the message to
|
||||
if job_kwargs.get("channel_id"):
|
||||
msg += f"**Channel**: <#{job_kwargs.get('channel_id')}>\n"
|
||||
msg += f"\n**Channel**: <#{job_kwargs.get('channel_id')}>"
|
||||
|
||||
# The Discord user to send the message to
|
||||
if job_kwargs.get("user_id"):
|
||||
msg += f"**User**: <@{job_kwargs.get('user_id')}>\n"
|
||||
msg += f"\n**User**: <@{job_kwargs.get('user_id')}>"
|
||||
|
||||
# The Discord guild to send the message to
|
||||
if job_kwargs.get("guild_id"):
|
||||
msg += f"**Guild**: {job_kwargs.get('guild_id')}\n"
|
||||
msg += f"\n**Guild**: {job_kwargs.get('guild_id')}"
|
||||
|
||||
logger.debug(f"Deletion message: {msg}")
|
||||
|
||||
@ -390,11 +390,15 @@ class JobManagementView(discord.ui.View):
|
||||
job_author: int = job_kwargs.get("author_id", 0)
|
||||
msg: str = f"Job '{job_msg}' has been {status} by <@{interaction.user.id}>. Job was created by <@{job_author}>."
|
||||
|
||||
# The time the job was supposed to run
|
||||
if hasattr(self.job, "next_run_time"):
|
||||
trigger_time: datetime.datetime | None = (
|
||||
self.job.trigger.run_date if isinstance(self.job.trigger, DateTrigger) else self.job.next_run_time
|
||||
)
|
||||
msg += f"\nNext run time: {trigger_time} {calculate(self.job)}"
|
||||
if self.job.next_run_time:
|
||||
msg += f"\n**Next run time**: {self.job.next_run_time} ({calculate(self.job)})"
|
||||
else:
|
||||
msg += "\n**Next run time**: Paused"
|
||||
msg += f"\n**Trigger**: {self.job.trigger}"
|
||||
else:
|
||||
msg += "\n**Next run time**: Pending"
|
||||
|
||||
await interaction.followup.send(msg)
|
||||
|
||||
@ -416,5 +420,5 @@ class JobManagementView(discord.ui.View):
|
||||
bool: Whether the interaction is valid.
|
||||
"""
|
||||
logger.info(f"Checking interaction for job: {self.job.id}")
|
||||
self.update_buttons()
|
||||
# self.update_buttons()
|
||||
return True
|
||||
|
Reference in New Issue
Block a user