Update Makefile, README and service file for remindme_caldav

- The Makefile now includes installation instructions for Debian/Ubuntu based systems.
- The README has been updated with more detailed descriptions of the script's purpose, how it works, and how to use it.
- The service file has been added to manage the remindme_caldav daemon on a systemd-based system.
This commit is contained in:
Sam 2024-02-15 15:21:37 +00:00
parent 740ac039dd
commit 8fe759673d
4 changed files with 100 additions and 61 deletions

18
Makefile Normal file
View File

@ -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

View File

@ -1,3 +1,67 @@
# remindme_caldav # 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.

View File

@ -131,15 +131,6 @@ class FileChangeHandler(FileSystemEventHandler):
class RecurringEventGenerator: class RecurringEventGenerator:
""" """
A class to generate recurring events based on a start date and a recurrence rule. 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): def __init__(self, dtstart, rrule):
self.dtstart = dtstart self.dtstart = dtstart
@ -212,15 +203,6 @@ class CalendarParser:
def parse_calendar(self, cal_str): def parse_calendar(self, cal_str):
""" """
Parse a calendar string and process each event. 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 # Parse the calendar
cal = self.parse_icalendar(cal_str) cal = self.parse_icalendar(cal_str)
@ -261,15 +243,6 @@ class CalendarParser:
def parse_icalendar(self, cal_str): def parse_icalendar(self, cal_str):
""" """
Parse a calendar string into an iCalendar object. 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: try:
return Calendar.from_ical(cal_str) return Calendar.from_ical(cal_str)
@ -279,12 +252,6 @@ class CalendarParser:
def process_event(self, event): def process_event(self, event):
""" """
Process an event from a parsed calendar and extract relevant information. 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 = { event_info = {
"uid": None, "uid": None,
@ -307,15 +274,6 @@ class CalendarParser:
def dtstart_to_datetime(self, dtstart): def dtstart_to_datetime(self, dtstart):
""" """
Convert a date or datetime object into a datetime object with UTC timezone. 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 # Ensure dates are always as datetime
try: try:
@ -329,16 +287,6 @@ class CalendarParser:
def remove_exdates(self, exdates, recur_dates): def remove_exdates(self, exdates, recur_dates):
""" """
Remove dates from a list of recurring event dates that are in the exdate list. 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 != []: if exdates != []:
try: try:
@ -355,12 +303,6 @@ class CalendarParser:
def process_valarm(self, event): def process_valarm(self, event):
""" """
Process VALARM components from an iCalendar event and extract trigger times. 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 = [] valarms = []
for subcomponent in event.walk("valarm"): for subcomponent in event.walk("valarm"):
@ -371,7 +313,7 @@ class CalendarParser:
def get_next_alert(event, current_time): 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"] event_dates = event["event_dates"]
valarm_deltas = event["valarms"] 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): 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 current_time >= next_alert and current_time < next_alert + dt.timedelta(seconds=15):
if len(event["alert_history"]) == 0: if len(event["alert_history"]) == 0:

15
remindme_caldav.service Normal file
View File

@ -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