From 569ceccb939d141cb784ff3590b2b0fe8ce9d4ff Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Tue, 18 Aug 2026 18:22:46 +0200 Subject: [PATCH] new feature: weekly blacklistwords --- .env.example | 1 + AGENTS.md | 3 ++- sync.py | 34 ++++++++++++++++++++++++++++------ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/.env.example b/.env.example index b17a7ae..0bd6a0f 100644 --- a/.env.example +++ b/.env.example @@ -46,3 +46,4 @@ API_PORT=8000 # WEEKLY_NOTIFY_TIMEZONE=Europe/Berlin # WEEKLY_NOTIFY_EMAIL=empfaenger@example.com # WEEKLY_SEARCHWORDS=Termin1,Termin2 +# WEEKLY_BLACKLISTWORDS=Ausgeschlossen1,Ausgeschlossen2 diff --git a/AGENTS.md b/AGENTS.md index 6b8e012..4fe7261 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -75,13 +75,14 @@ Sendet täglich um konfigurierte Uhrzeit eine HTML-Email mit anstehenden Termine ### Wöchentliche E-Mail-Benachrichtigung (Vorab-Info) Sendet wöchentlich (standardmäßig Freitags) eine HTML-Email mit Terminen, die auf Suchbegriffe passen: - Sucht nach Begriffen in summary, description und location +- Optionale Blacklist: Begriffe die ausgeschlossen werden (z.B. `WEEKLY_BLACKLISTWORDS=Ausgeschlossen1,Ausgeschlossen2`) - Zeitraum: Samstag bis Freitag der nächsten Woche - Subject: Bei 1 Termin "Vorab-Info: Termin am Mo, DD.MM.", bei mehreren "Vorab-Info: X Termine nächste Woche" - Tracking via `weekly_notification_log` Tabelle (verhindert Doppelversand) - Nur Termine mit Uhrzeit (`all_day=0`), keine Ganztagstermine - Keine Email wenn keine Treffer - Konfiguration über WEEKLY_* Umgebungsvariablen -- Funktionen: `parse_weekly_searchwords()`, `get_weekly_events()`, `should_send_weekly()`, `send_weekly_notification()`, `log_weekly_notification()`, `check_and_send_weekly_notification()` +- Funktionen: `parse_weekly_searchwords()`, `parse_weekly_blacklistwords()`, `get_weekly_events()`, `should_send_weekly()`, `send_weekly_notification()`, `log_weekly_notification()`, `check_and_send_weekly_notification()` ## Entwicklung diff --git a/sync.py b/sync.py index c64d143..0e7e12b 100644 --- a/sync.py +++ b/sync.py @@ -67,6 +67,7 @@ WEEKLY_NOTIFY_TIME = int(os.environ.get("WEEKLY_NOTIFY_TIME", "16")) WEEKLY_NOTIFY_TIMEZONE = os.environ.get("WEEKLY_NOTIFY_TIMEZONE", "Europe/Berlin") WEEKLY_NOTIFY_EMAIL = os.environ.get("WEEKLY_NOTIFY_EMAIL", "") WEEKLY_SEARCHWORDS = os.environ.get("WEEKLY_SEARCHWORDS", "") +WEEKLY_BLACKLISTWORDS = os.environ.get("WEEKLY_BLACKLISTWORDS", "") def bootstrap_database(): @@ -436,7 +437,13 @@ def parse_weekly_searchwords(): return [w.strip() for w in WEEKLY_SEARCHWORDS.split(",") if w.strip()] -def get_weekly_events(conn, calendar_label, searchwords): +def parse_weekly_blacklistwords(): + if not WEEKLY_BLACKLISTWORDS: + return [] + return [w.strip() for w in WEEKLY_BLACKLISTWORDS.split(",") if w.strip()] + + +def get_weekly_events(conn, calendar_label, searchwords, blacklistwords=None): tz = ZoneInfo(WEEKLY_NOTIFY_TIMEZONE) now = datetime.now(tz) today = now.replace(hour=0, minute=0, second=0, microsecond=0) @@ -459,6 +466,17 @@ def get_weekly_events(conn, calendar_label, searchwords): where_search = " OR ".join(search_clauses) if search_clauses else "1=1" + blacklist_clauses = [] + blacklist_params = [] + for word in (blacklistwords or []): + like = f"%{word}%" + blacklist_clauses.append("(summary LIKE %s OR description LIKE %s OR location LIKE %s)") + blacklist_params.extend([like, like, like]) + + where_blacklist = "" + if blacklist_clauses: + where_blacklist = f"AND NOT ({' OR '.join(blacklist_clauses)})" + cur = conn.cursor(dictionary=True) cur.execute( f""" @@ -470,9 +488,10 @@ def get_weekly_events(conn, calendar_label, searchwords): AND start_at >= %s AND start_at <= %s AND ({where_search}) + {where_blacklist} ORDER BY start_at ASC """, - (calendar_label, week_start_naive, week_end_naive, *search_params), + (calendar_label, week_start_naive, week_end_naive, *search_params, *blacklist_params), ) events = cur.fetchall() cur.close() @@ -510,7 +529,7 @@ def log_weekly_notification(conn, event_count): cur.close() -def send_weekly_notification(events, searchwords, week_start, week_end): +def send_weekly_notification(events, searchwords, week_start, week_end, blacklistwords=None): email = WEEKLY_NOTIFY_EMAIL or NOTIFY_EMAIL if not SMTP_HOST or not email: log.warning("SMTP-Konfiguration unvollstaendig, ueberspringe Wochenbenachrichtigung") @@ -543,6 +562,8 @@ def send_weekly_notification(events, searchwords, week_start, week_end): """ words_display = ", ".join(searchwords) + blacklist_display = ", ".join(blacklistwords) if blacklistwords else "" + blacklist_line = f"Ausgeschlossen: {blacklist_display}" if blacklist_display else "" html = f""" @@ -554,7 +575,7 @@ def send_weekly_notification(events, searchwords, week_start, week_end): {event_rows}
-

Suchbegriffe: {words_display}

+

Suchbegriffe: {words_display}{" " + blacklist_line if blacklist_line else ""}

""" @@ -590,9 +611,10 @@ def check_and_send_weekly_notification(): try: if should_send_weekly(conn): searchwords = parse_weekly_searchwords() - events, week_start, week_end = get_weekly_events(conn, CALENDAR_LABEL, searchwords) + blacklistwords = parse_weekly_blacklistwords() + events, week_start, week_end = get_weekly_events(conn, CALENDAR_LABEL, searchwords, blacklistwords) if events: - send_weekly_notification(events, searchwords, week_start, week_end) + send_weekly_notification(events, searchwords, week_start, week_end, blacklistwords) log_weekly_notification(conn, len(events)) log.info("Wochenbenachrichtigung fuer %d Events gesendet", len(events)) else: