Umgebungsvariable EVENT_YEARS für konfigurierbaren Event-Horizont hinzugefügt
All checks were successful
Build Test Docker Image / docker-test (pull_request) Successful in 1m30s
Run Tests / test (pull_request) Successful in 4m46s

This commit is contained in:
2026-03-29 22:47:56 +02:00
parent c1514330e3
commit ea598a979b
4 changed files with 33 additions and 4 deletions

View File

@@ -133,7 +133,7 @@ func (d *Daemon) syncBirthdaysToCal(ctx context.Context, httpClient webdav.HTTPC
return err
}
slog.DebugContext(ctx, "queried existing calendar events", "user", user, "count", len(events))
bevs := generateBirthdayEvents(birthdays)
bevs := generateBirthdayEvents(birthdays, d.eventYears)
slog.DebugContext(ctx, "generated birthday events", "user", user, "count", len(bevs))
bevsInSync := make([]int, 0)
driftedEvents := make([]string, 0)
@@ -185,11 +185,11 @@ type birthdayEvent struct {
DateTimeEnd string
}
func generateBirthdayEvents(birthdays []BirthdayContact) []birthdayEvent {
func generateBirthdayEvents(birthdays []BirthdayContact, eventYears int) []birthdayEvent {
cyear := time.Now().Year()
bb := make([]birthdayEvent, 0)
for _, v := range birthdays {
for year := cyear; year <= 10+cyear; year++ {
for year := cyear; year <= eventYears+cyear; year++ {
yearshift := year - v.Date.Year()
prefix := "\U0001F382 " // 🎂
if v.Type == ContactTypeAnniversary {

View File

@@ -8,6 +8,7 @@ import (
"net/http"
"os"
"os/signal"
"strconv"
"strings"
"sync"
"syscall"
@@ -39,6 +40,7 @@ type Daemon struct {
oldCalendarName string
notificationEnabled bool
notificationTrigger string
eventYears int
syncInterval time.Duration
excludeMailboxes map[string]bool
health *healthState
@@ -120,6 +122,11 @@ func run() error {
calendarName = "Birthdays"
}
notificationEnabled := strings.EqualFold(os.Getenv("NOTIFICATION_ENABLED"), "true")
eventYears, err := parseEventYears()
if err != nil {
return err
}
slog.Info("event horizon configured", "years", eventYears)
syncInterval, err := parseSyncInterval()
if err != nil {
return err
@@ -129,6 +136,7 @@ func run() error {
"MAILCOW_BASE", mailcowBase,
"CALENDAR_NAME", calendarName,
"NOTIFICATION_ENABLED", notificationEnabled,
"EVENT_YEARS", eventYears,
"MAILCOW_RESOLVE_HOST", os.Getenv("MAILCOW_RESOLVE_HOST"),
"STATEFILE", os.Getenv("STATEFILE"),
"MAILBOX_EXCLUDE", os.Getenv("MAILBOX_EXCLUDE"),
@@ -166,6 +174,7 @@ func run() error {
calendarName: calendarName,
notificationEnabled: notificationEnabled,
notificationTrigger: notificationTrigger,
eventYears: eventYears,
syncInterval: syncInterval,
excludeMailboxes: excludeMailboxes,
}
@@ -420,6 +429,24 @@ func parseSyncInterval() (time.Duration, error) {
return d, nil
}
// parseEventYears liest EVENT_YEARS aus der Umgebung und gibt die Anzahl
// der Jahre zurück, für die Geburtstags-Events im Voraus erzeugt werden.
// Standard: 10. Gültige Werte: 130.
func parseEventYears() (int, error) {
raw := os.Getenv("EVENT_YEARS")
if raw == "" {
return 10, nil
}
n, err := strconv.Atoi(raw)
if err != nil {
return 0, fmt.Errorf("invalid EVENT_YEARS %q: %w", raw, err)
}
if n < 1 || n > 30 {
return 0, fmt.Errorf("EVENT_YEARS must be between 1 and 30, got %d", n)
}
return n, nil
}
// buildTransport erstellt einen http.Transport.
// Wenn MAILCOW_RESOLVE_HOST gesetzt ist (z. B. "nginx-mailcow"), wird der
// tatsächliche TCP-Connect auf diesen Host umgeleitet, während TLS-SNI und

View File

@@ -17,7 +17,7 @@ Der Mailcow Birthday Daemon synchronisiert automatisch Geburtstagskalender für
- Die daraus resultierenden Kalendereinträge werden im Voraus berechnet.
> **Hinweis zu Jahrestagen:** Die SOGo-Weboberfläche bietet kein Feld zum Anzeigen oder Bearbeiten von Jahrestagen. Das `ANNIVERSARY`-Feld muss über einen externen Client (z. B. Thunderbird, Android- oder iOS-Kontakte-App) gepflegt und per CardDAV synchronisiert werden. Das SOGo-CardDAV-Backend speichert und liefert das Feld korrekt es fehlt lediglich die Unterstützung in der Web-UI.
- Aktuell fest eingestellt: 1 Jahr in der Vergangenheit, 10 Jahre in der Zukunft.
- Aktuell standardmäßig: 1 Jahr in der Vergangenheit, 10 Jahre in der Zukunft (konfigurierbar über `EVENT_YEARS`).
- Selbstverständlich pro Mailbox isoliert ein Benutzer sieht nur die Geburtstage seiner eigenen Kontakte.
## Mailbox-Filter

View File

@@ -28,6 +28,7 @@ services:
# - NOTIFICATION_ENABLED=true
# - NOTIFICATION_TIME=08:00
# - SYNC_INTERVAL=15m
# - EVENT_YEARS=10
# - MAILBOX_EXCLUDE=user1@example.com,user2@example.com
# - LOG_LEVEL=info
volumes:
@@ -66,6 +67,7 @@ docker compose pull birthdaydaemon && docker compose up -d --no-deps birthdaydae
| `NOTIFICATION_TIME` | Nein | `08:00` | Uhrzeit der Benachrichtigung im Format `HH:MM` (nur wirksam wenn `NOTIFICATION_ENABLED=true`) |
| `STATEFILE` | Nein | `state.json` (im Container: `/data/state.json`) | Pfad zur Zustandsdatei, in der App-Passwörter und der aktuelle Kalendername gespeichert werden |
| `SYNC_INTERVAL` | Nein | `15m` | Intervall zwischen den Synchronisationsläufen im Go-Duration-Format (z. B. `10m`, `30m`, `1h`). Mindestwert: `1m`. |
| `EVENT_YEARS` | Nein | `10` | Anzahl der Jahre, für die Geburtstags-Events im Voraus erzeugt werden. Gültige Werte: `1``30`. Kleinere Werte können die Performance in Kalender-Clients verbessern. |
| `MAILBOX_EXCLUDE` | Nein | | Kommagetrennte Liste von Mailadressen, die von der Synchronisation ausgeschlossen werden (z. B. `admin@example.com,noreply@example.com`). Standardmäßig erhalten alle aktiven Mailboxen einen Geburtstagskalender. |
| `LOG_LEVEL` | Nein | `info` | Log-Verbosity: `debug`, `info`, `warn` oder `error`. Im `debug`-Modus werden einzelne Kalendereinträge (hinzugefügt/entfernt) und Kontaktdetails geloggt. |
## API-Key erstellen