diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e35d58c --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +install: + sudo apt-get update && sudo apt-get install -y python3.11 python3.11-venv + python3.11 -m venv /opt/remindme_caldav/.venv + source /opt/remindme_caldav/.venv/bin/activate && pip install --upgrade pip && pip install -r requirements.txt + cp remindme_caldav.py /opt/remindme_caldav/ + sudo cp remindme_caldav.service /etc/systemd/system/ + sudo systemctl daemon-reload + sudo systemctl enable remindme_caldav.service + sudo systemctl start remindme_caldav.service + +uninstall: + sudo systemctl stop remindme_caldav.service + sudo systemctl disable remindme_caldav.service + rm -rf /opt/remindme_caldav + rm /etc/systemd/system/remindme_caldav.service + +clean: + deactivate diff --git a/README.md b/README.md index fa895a4..f359361 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,67 @@ # remindme_caldav +## A Calendar Alerting Daemon + +## Purpose +This script is a simple calendar alerting daemon written in Python. It monitors +.ics files for changes and sends alerts based on the events' start times, +recurrence rules, and alert triggers defined within these files. The main +purpose of this script is to provide reminders or notifications about upcoming +events. + +## How it Works +The script works by parsing .ics files using the `icalendar` library, which +converts them into a Python dictionary format for easier manipulation. It then +processes each event and calculates when the next alert should be triggered +based on the event's start time, recurrence rules, and alert triggers. If an +alert is due to trigger, it sends a notification via email or XMPP (an instant +messaging protocol). + +The script also monitors for changes in the directory containing .ics files +using the `watchdog` library. When a file is modified, created, or deleted, it +updates its internal list of events accordingly. + +## How to Use It +This script should be used with a calendar syncing service such as vdirsyncer. +vdirsyncer can be scheduled using cron to sync regularly from the CalDav +server. + +To use this script, you need Python 3 installed on your system. You can install +the required libraries by running: + +```bash +pip install -r requirements.txt +``` + +You also need a .toml configuration file with the following structure: +```toml +[app] +calendar_dir = "/path/to/your/ics/files" +email_address = "your-email@example.com" +smtp_server = "smtp.example.com" +smtp_port = 587 +smtp_username = "your-username" +smtp_password = "your-password" +... +``` + +You can then run the script with: +```bash +python3 remindme_caldav.py --config /path/to/your/config.toml +``` + +## Installation +A Makefile and systemd service file is also included for Debian/Ubuntu based +systems. + +This Makefile does the following: + - install: Installs Python 3.11, creates a virtual environment in + /opt/remindme_caldav/.venv, installs dependencies from requirements.txt + into this virtual environment, copies your script to /opt/remindme_caldav/, + and sets up the systemd service file. + + - uninstall: Stops and disables the systemd service, removes the installation + directory (/opt/remindme_caldav/), and deletes the systemd service file. + + - clean: Deactivates the virtual environment. + -A simple script to send alerts/reminders for caldav events. \ No newline at end of file diff --git a/remindme_caldav.py b/remindme_caldav.py index 60257f3..05a79d0 100644 --- a/remindme_caldav.py +++ b/remindme_caldav.py @@ -131,15 +131,6 @@ class FileChangeHandler(FileSystemEventHandler): class RecurringEventGenerator: """ A class to generate recurring events based on a start date and a recurrence rule. - - Attributes: - dtstart (datetime): The starting date of the event series. - rrule (rrule): The recurrence rule for the event series. - - Methods: - __init__(self, dtstart, rrule): Initializes the class with a start date and a recurrence rule. - generate(self): Generates the recurring events based on the start date and recurrence rule. - Returns a dictionary containing information about the recurring events. """ def __init__(self, dtstart, rrule): self.dtstart = dtstart @@ -212,15 +203,6 @@ class CalendarParser: def parse_calendar(self, cal_str): """ Parse a calendar string and process each event. - - Args: - cal_str (str): The iCalendar string to be parsed. - - Returns: - dict: A dictionary containing information about the processed events. - - Raises: - RuntimeError: If there are no dates returned for an event or if there is an error calculating the event hash. """ # Parse the calendar cal = self.parse_icalendar(cal_str) @@ -261,15 +243,6 @@ class CalendarParser: def parse_icalendar(self, cal_str): """ Parse a calendar string into an iCalendar object. - - Args: - cal_str (str): The iCalendar string to be parsed. - - Returns: - Calendar: An iCalendar object representing the parsed calendar. - - Raises: - RuntimeError: If there is an error parsing the calendar. """ try: return Calendar.from_ical(cal_str) @@ -279,12 +252,6 @@ class CalendarParser: def process_event(self, event): """ Process an event from a parsed calendar and extract relevant information. - - Args: - event (Event): An iCalendar event object to be processed. - - Returns: - dict: A dictionary containing the extracted event information. """ event_info = { "uid": None, @@ -307,15 +274,6 @@ class CalendarParser: def dtstart_to_datetime(self, dtstart): """ Convert a date or datetime object into a datetime object with UTC timezone. - - Args: - dtstart (date/datetime): The date or datetime to be converted. - - Returns: - datetime: A datetime object representing the input date or datetime in UTC timezone. - - Raises: - RuntimeError: If there is an error converting the input to a datetime object. """ # Ensure dates are always as datetime try: @@ -329,16 +287,6 @@ class CalendarParser: def remove_exdates(self, exdates, recur_dates): """ Remove dates from a list of recurring event dates that are in the exdate list. - - Args: - exdates (list): A list of datetime objects representing excluded dates. - recur_dates (list): A list of datetime objects representing recurring event dates. - - Returns: - list: A list of datetime objects representing the remaining recurring event dates after removing the exdate dates. - - Raises: - RuntimeError: If there is an error processing the exdates. """ if exdates != []: try: @@ -355,12 +303,6 @@ class CalendarParser: def process_valarm(self, event): """ Process VALARM components from an iCalendar event and extract trigger times. - - Args: - event (Event): An iCalendar event object to be processed. - - Returns: - list: A list of datetime objects representing the extracted trigger times. """ valarms = [] for subcomponent in event.walk("valarm"): @@ -371,7 +313,7 @@ class CalendarParser: def get_next_alert(event, current_time): """ - This function returns the next alert that should be processed based on the current time. + Returns the next alert that should be processed based on the current time. """ event_dates = event["event_dates"] valarm_deltas = event["valarms"] @@ -387,7 +329,7 @@ def get_next_alert(event, current_time): def process_alert(current_time, next_alert, next_event, event, config): """ - This function processes a given alert and passes it to a messaging client. + Processes a given alert and passes it to a messaging client. """ if current_time >= next_alert and current_time < next_alert + dt.timedelta(seconds=15): if len(event["alert_history"]) == 0: diff --git a/remindme_caldav.service b/remindme_caldav.service new file mode 100644 index 0000000..62395ac --- /dev/null +++ b/remindme_caldav.service @@ -0,0 +1,15 @@ +[Unit] +Description=Calendar Alerting Daemon +After=network.target +StartLimitIntervalSec=0 + +[Service] +Type=simple +Restart=always +RestartSec=1 +User=root +ExecStart=/opt/remindme_caldav/.venv/bin/python3 /opt/remindme_caldav/remindme_caldav.py --config /etc/remindme_caldav/config.toml + +[Install] +WantedBy=multi-user.target +