Improve message sent to Discord for missed reminder
This commit is contained in:
@ -15,6 +15,15 @@ TIMEZONE=
|
|||||||
# On Linux you will need to use double slashes before the path to get the absolute path.
|
# On Linux you will need to use double slashes before the path to get the absolute path.
|
||||||
# SQLITE_LOCATION=//home/lovinator/foo.db
|
# SQLITE_LOCATION=//home/lovinator/foo.db
|
||||||
|
|
||||||
|
# Additional directory to store data in.
|
||||||
|
# Note: You will still need to set the SQLITE_LOCATION to a valid path.
|
||||||
|
# This is used to store markdown files with the reminder data.
|
||||||
|
# The directory will be created if it does not exist.
|
||||||
|
# Example: DATA_DIR=C:/Code/discord-reminder-bot/data
|
||||||
|
# Example: DATA_DIR=/home/lovinator/data
|
||||||
|
# Example: DATA_DIR=./data
|
||||||
|
DATA_DIR=./data
|
||||||
|
|
||||||
# Log level, CRITICAL, ERROR, WARNING, INFO, DEBUG.
|
# Log level, CRITICAL, ERROR, WARNING, INFO, DEBUG.
|
||||||
LOG_LEVEL=INFO
|
LOG_LEVEL=INFO
|
||||||
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -209,3 +209,4 @@ __marimo__/
|
|||||||
# SQLite
|
# SQLite
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite.*
|
*.sqlite.*
|
||||||
|
data/reminder_data/*
|
||||||
|
@ -18,5 +18,6 @@ RUN --mount=type=cache,target=/root/.cache/uv \
|
|||||||
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
||||||
uv sync --no-install-project
|
uv sync --no-install-project
|
||||||
|
|
||||||
|
ARG DATA_DIR=${DATA_DIR:-/home/botuser/data}
|
||||||
VOLUME ["/home/botuser/data/"]
|
VOLUME ["/home/botuser/data/"]
|
||||||
CMD ["uv", "run", "python", "-m", "discord_reminder_bot.main"]
|
CMD ["uv", "run", "python", "-m", "discord_reminder_bot.main"]
|
||||||
|
@ -26,7 +26,7 @@ from loguru import logger
|
|||||||
|
|
||||||
from discord_reminder_bot.helpers import calculate, generate_markdown_state, generate_state, get_human_readable_time, parse_time
|
from discord_reminder_bot.helpers import calculate, generate_markdown_state, generate_state, get_human_readable_time, parse_time
|
||||||
from discord_reminder_bot.modals import CronReminderModifyModal, DateReminderModifyModal, IntervalReminderModifyModal
|
from discord_reminder_bot.modals import CronReminderModifyModal, DateReminderModifyModal, IntervalReminderModifyModal
|
||||||
from discord_reminder_bot.settings import scheduler
|
from discord_reminder_bot.settings import export_reminder_jobs_to_markdown, get_markdown_contents_from_markdown_file, scheduler
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
@ -44,9 +44,21 @@ def my_listener(event: JobExecutionEvent) -> None:
|
|||||||
Args:
|
Args:
|
||||||
event: The event that occurred.
|
event: The event that occurred.
|
||||||
"""
|
"""
|
||||||
|
if event.code == events.EVENT_JOB_ADDED:
|
||||||
|
export_reminder_jobs_to_markdown()
|
||||||
|
|
||||||
if event.code == events.EVENT_JOB_MISSED:
|
if event.code == events.EVENT_JOB_MISSED:
|
||||||
scheduled_time: str = event.scheduled_run_time.strftime("%Y-%m-%d %H:%M:%S")
|
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}"
|
|
||||||
|
# Get data from markdown file that was created by export_reminder_jobs_to_markdown()
|
||||||
|
job_data: str = get_markdown_contents_from_markdown_file(event.job_id)
|
||||||
|
if not job_data:
|
||||||
|
msg: str = f"Job {event.job_id} was missed! Was scheduled at {scheduled_time}"
|
||||||
|
logger.warning(msg)
|
||||||
|
else:
|
||||||
|
msg: str = f"Job {event.job_id} was missed! Was scheduled at {scheduled_time}\nData:\n```json\n{job_data}\n```"
|
||||||
|
logger.warning(msg)
|
||||||
|
|
||||||
send_webhook(message=msg)
|
send_webhook(message=msg)
|
||||||
|
|
||||||
if event.exception:
|
if event.exception:
|
||||||
@ -120,6 +132,8 @@ class RemindBotClient(discord.Client):
|
|||||||
else:
|
else:
|
||||||
logger.error("Scheduler is already running.")
|
logger.error("Scheduler is already running.")
|
||||||
|
|
||||||
|
export_reminder_jobs_to_markdown()
|
||||||
|
|
||||||
|
|
||||||
def format_job_for_ui(job: Job) -> str:
|
def format_job_for_ui(job: Job) -> str:
|
||||||
"""Format a single job for display in the UI.
|
"""Format a single job for display in the UI.
|
||||||
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
from pathlib import Path
|
||||||
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
|
||||||
|
|
||||||
import pytz
|
import pytz
|
||||||
@ -11,6 +12,8 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
|
|||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
|
|
||||||
|
from discord_reminder_bot.helpers import generate_state
|
||||||
|
|
||||||
load_dotenv(verbose=True)
|
load_dotenv(verbose=True)
|
||||||
|
|
||||||
default_sentry_dsn: str = "https://c4c61a52838be9b5042144420fba5aaa@o4505228040339456.ingest.us.sentry.io/4508707268984832"
|
default_sentry_dsn: str = "https://c4c61a52838be9b5042144420fba5aaa@o4505228040339456.ingest.us.sentry.io/4508707268984832"
|
||||||
@ -57,3 +60,40 @@ def get_scheduler() -> AsyncIOScheduler:
|
|||||||
|
|
||||||
|
|
||||||
scheduler: AsyncIOScheduler = get_scheduler()
|
scheduler: AsyncIOScheduler = get_scheduler()
|
||||||
|
|
||||||
|
|
||||||
|
def export_reminder_jobs_to_markdown() -> None:
|
||||||
|
"""Loop through the APScheduler database and save each job's data to a markdown file if changed."""
|
||||||
|
data_dir: str = os.getenv("DATA_DIR", default="./data")
|
||||||
|
logger.info(f"Exporting reminder jobs to markdown files in directory: {data_dir}")
|
||||||
|
|
||||||
|
for job in scheduler.get_jobs():
|
||||||
|
job_state: str = generate_state(job.__getstate__(), job)
|
||||||
|
file_path: Path = Path(data_dir) / "reminder_data" / f"{job.id}.md"
|
||||||
|
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
try:
|
||||||
|
if file_path.exists():
|
||||||
|
existing_content = file_path.read_text(encoding="utf-8")
|
||||||
|
if existing_content == job_state:
|
||||||
|
logger.debug(f"No changes for {file_path}, skipping write.")
|
||||||
|
continue
|
||||||
|
file_path.write_text(job_state, encoding="utf-8")
|
||||||
|
logger.info(f"Data saved to {file_path}")
|
||||||
|
except OSError as e:
|
||||||
|
logger.error(f"Failed to save data to {file_path}: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_markdown_contents_from_markdown_file(job_id: str) -> str:
|
||||||
|
"""Get the contents of a markdown file for a specific job ID.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
job_id (str): The ID of the job.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The contents of the markdown file, or an empty string if the file does not exist.
|
||||||
|
"""
|
||||||
|
data_dir: str = os.getenv("DATA_DIR", default="./data")
|
||||||
|
file_path: Path = Path(data_dir) / "reminder_data" / f"{job_id}.md"
|
||||||
|
if file_path.exists():
|
||||||
|
return file_path.read_text(encoding="utf-8")
|
||||||
|
return ""
|
||||||
|
@ -9,6 +9,7 @@ services:
|
|||||||
- TIMEZONE=${TIMEZONE}
|
- TIMEZONE=${TIMEZONE}
|
||||||
- LOG_LEVEL=${LOG_LEVEL}
|
- LOG_LEVEL=${LOG_LEVEL}
|
||||||
- SQLITE_LOCATION=/data/jobs.sqlite
|
- SQLITE_LOCATION=/data/jobs.sqlite
|
||||||
|
- DATA_DIR=/data
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- data_folder:/home/botuser/data/
|
- data_folder:/home/botuser/data/
|
||||||
|
Reference in New Issue
Block a user