new feature: weekly blacklistwords

This commit is contained in:
2026-08-18 18:22:46 +02:00
parent 3fccf6df67
commit 569ceccb93
3 changed files with 31 additions and 7 deletions
+1
View File
@@ -46,3 +46,4 @@ API_PORT=8000
# WEEKLY_NOTIFY_TIMEZONE=Europe/Berlin # WEEKLY_NOTIFY_TIMEZONE=Europe/Berlin
# WEEKLY_NOTIFY_EMAIL=empfaenger@example.com # WEEKLY_NOTIFY_EMAIL=empfaenger@example.com
# WEEKLY_SEARCHWORDS=Termin1,Termin2 # WEEKLY_SEARCHWORDS=Termin1,Termin2
# WEEKLY_BLACKLISTWORDS=Ausgeschlossen1,Ausgeschlossen2
+2 -1
View File
@@ -75,13 +75,14 @@ Sendet täglich um konfigurierte Uhrzeit eine HTML-Email mit anstehenden Termine
### Wöchentliche E-Mail-Benachrichtigung (Vorab-Info) ### Wöchentliche E-Mail-Benachrichtigung (Vorab-Info)
Sendet wöchentlich (standardmäßig Freitags) eine HTML-Email mit Terminen, die auf Suchbegriffe passen: Sendet wöchentlich (standardmäßig Freitags) eine HTML-Email mit Terminen, die auf Suchbegriffe passen:
- Sucht nach Begriffen in summary, description und location - 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 - 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" - 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) - Tracking via `weekly_notification_log` Tabelle (verhindert Doppelversand)
- Nur Termine mit Uhrzeit (`all_day=0`), keine Ganztagstermine - Nur Termine mit Uhrzeit (`all_day=0`), keine Ganztagstermine
- Keine Email wenn keine Treffer - Keine Email wenn keine Treffer
- Konfiguration über WEEKLY_* Umgebungsvariablen - 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 ## Entwicklung
+28 -6
View File
@@ -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_TIMEZONE = os.environ.get("WEEKLY_NOTIFY_TIMEZONE", "Europe/Berlin")
WEEKLY_NOTIFY_EMAIL = os.environ.get("WEEKLY_NOTIFY_EMAIL", "") WEEKLY_NOTIFY_EMAIL = os.environ.get("WEEKLY_NOTIFY_EMAIL", "")
WEEKLY_SEARCHWORDS = os.environ.get("WEEKLY_SEARCHWORDS", "") WEEKLY_SEARCHWORDS = os.environ.get("WEEKLY_SEARCHWORDS", "")
WEEKLY_BLACKLISTWORDS = os.environ.get("WEEKLY_BLACKLISTWORDS", "")
def bootstrap_database(): def bootstrap_database():
@@ -436,7 +437,13 @@ def parse_weekly_searchwords():
return [w.strip() for w in WEEKLY_SEARCHWORDS.split(",") if w.strip()] 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) tz = ZoneInfo(WEEKLY_NOTIFY_TIMEZONE)
now = datetime.now(tz) now = datetime.now(tz)
today = now.replace(hour=0, minute=0, second=0, microsecond=0) 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" 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 = conn.cursor(dictionary=True)
cur.execute( cur.execute(
f""" f"""
@@ -470,9 +488,10 @@ def get_weekly_events(conn, calendar_label, searchwords):
AND start_at >= %s AND start_at >= %s
AND start_at <= %s AND start_at <= %s
AND ({where_search}) AND ({where_search})
{where_blacklist}
ORDER BY start_at ASC 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() events = cur.fetchall()
cur.close() cur.close()
@@ -510,7 +529,7 @@ def log_weekly_notification(conn, event_count):
cur.close() 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 email = WEEKLY_NOTIFY_EMAIL or NOTIFY_EMAIL
if not SMTP_HOST or not email: if not SMTP_HOST or not email:
log.warning("SMTP-Konfiguration unvollstaendig, ueberspringe Wochenbenachrichtigung") log.warning("SMTP-Konfiguration unvollstaendig, ueberspringe Wochenbenachrichtigung")
@@ -543,6 +562,8 @@ def send_weekly_notification(events, searchwords, week_start, week_end):
</tr>""" </tr>"""
words_display = ", ".join(searchwords) words_display = ", ".join(searchwords)
blacklist_display = ", ".join(blacklistwords) if blacklistwords else ""
blacklist_line = f"Ausgeschlossen: {blacklist_display}" if blacklist_display else ""
html = f""" html = f"""
<!DOCTYPE html> <!DOCTYPE html>
@@ -554,7 +575,7 @@ def send_weekly_notification(events, searchwords, week_start, week_end):
<table style="width: 100%; border-collapse: collapse; margin: 20px 0; background: #f9f9f9; border-radius: 8px; overflow: hidden;"> <table style="width: 100%; border-collapse: collapse; margin: 20px 0; background: #f9f9f9; border-radius: 8px; overflow: hidden;">
{event_rows} {event_rows}
</table> </table>
<p style="color: #888; font-size: 12px;">Suchbegriffe: {words_display}</p> <p style="color: #888; font-size: 12px;">Suchbegriffe: {words_display}{" " + blacklist_line if blacklist_line else ""}</p>
</body> </body>
</html> </html>
""" """
@@ -590,9 +611,10 @@ def check_and_send_weekly_notification():
try: try:
if should_send_weekly(conn): if should_send_weekly(conn):
searchwords = parse_weekly_searchwords() 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: 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_weekly_notification(conn, len(events))
log.info("Wochenbenachrichtigung fuer %d Events gesendet", len(events)) log.info("Wochenbenachrichtigung fuer %d Events gesendet", len(events))
else: else: