dumbremind/caldav-reminder.py
2025-09-13 17:15:46 +02:00

80 lines
2.3 KiB
Python

#!/usr/bin/python3
"""calendar reminder
this logs into a caldav server and get a digest of all of the
events scheduled for next days. Good candidate to run from cron
"""
import sys
import os
from dotenv import load_dotenv
from datetime import timedelta, date
from os import environ
from urllib.parse import quote_plus
import caldav
load_dotenv()
# read parameters from dot env
USERNAME = os.getenv('CALDAV_USERNAME')
PASSWORD = os.getenv('CALDAV_PASSWORD')
BASE_URL = os.getenv('CALDAV_URL')
NEXT_DAYS = int(os.getenv('NEXT_DAYS'))
def fill_event(component, calendar) -> dict[str, str]:
# quite some data is tossed away here - like, the recurring rule.
cur = {}
cur["calendar"] = f"{calendar}"
cur["summary"] = component.get("summary")
cur["description"] = component.get("description")
# month/day/year time? Never ever do that!
# It's one of the most confusing date formats ever!
# Use year-month-day time instead ... https://xkcd.com/1179/
cur["start"] = component.start.strftime("%m/%d/%Y %H:%M")
endDate = component.end
if endDate:
cur["end"] = endDate.strftime("%m/%d/%Y %H:%M")
return cur
def fill_todo(component, calendar) -> dict[str, str]:
cur = {}
cur["calendar"] = f"{calendar}"
cur["summary"] = component.get("summary")
cur["description"] = component.get("description")
return cur
def display_event(event: dict[str, str]):
print(event["start"] + " " + event["calendar"] + "/" + event["summary"])
def display_todo(todo: dict[str, str]):
print("Todo : " + todo["summary"])
url = 'https://' + quote_plus(USERNAME) + ":" + quote_plus(PASSWORD) \
+ '@' + BASE_URL + quote_plus(USERNAME) + '/'
start = date.today()
end = start + timedelta(days=NEXT_DAYS)
client = caldav.DAVClient(url)
calendars = client.principal().calendars()
if not calendars:
print("No calendars defined for " + USERNAME)
sys.exit(0)
for cal in calendars:
events = cal.search(event=True, start=start, end=end, expand=True) \
+ cal.search(todo=True)
for event in events:
for component in event.icalendar_instance.walk():
if component.name == "VEVENT":
display_event(fill_event(component, cal))
elif component.name == "VTODO":
display_todo(fill_todo(component, cal))
print(".")