From 599d53e0eafe266adf6b0b9b0c442969d07c5b4c Mon Sep 17 00:00:00 2001 From: Sandro Date: Sat, 31 Oct 2026 12:20:37 +0100 Subject: [PATCH] fix(mail): greeting follows the run time, not the clock (CI failed from noon on) Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + internal/api/notify.go | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c000595..fe2ad8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Das Format folgt [Keep a Changelog](https://keepachangelog.com/de/1.1.0/), die V ### Fixed - Push-Benachrichtigungen: Nach dem Neuinstallieren der App zählte die alte Installation als weiteres Gerät mit („2 aktiv“), bis der nächste Push sie aussortierte. Schaltet ein Gerät Push neu ein, meldet es jetzt seine alte Anmeldung mit ab. Außerdem zeigen die Einstellungen alle Geräte einzeln (mit Datum und „dieses Gerät“) und jedes lässt sich entfernen. +- Tageszusammenfassung und Neuigkeiten per E-Mail: Die Begrüßung („Guten Morgen“) richtete sich nach der Uhr beim Versand statt nach der Zeit, für die die Nachricht gedacht ist. Dadurch schlug ab mittags auch ein Test fehl, und der Build von 0.22.1 brach ab – die Version kommt erst mit diesem Fix an. ## [0.22.0] - 2026-10-31 diff --git a/internal/api/notify.go b/internal/api/notify.go index 9241685..1a0a644 100644 --- a/internal/api/notify.go +++ b/internal/api/notify.go @@ -176,7 +176,7 @@ func (s *Server) flushNotifications(now time.Time) { item := notifyItem{kind: q.Kind, title: q.Payload["title"], text: q.Payload["text"]} out.add(set.Channels(q.Kind), item) } - if err := s.deliver(uid, "Neuigkeiten", out, false); err != nil { + if err := s.deliver(uid, "Neuigkeiten", out, false, now); err != nil { if maxAttempts+1 >= notifyMaxAttempts { slog.Warn("Benachrichtigungen nach mehreren Fehlversuchen verworfen", "user_id", uid, "count", len(ids), "error", err) _ = s.store.DeleteQueuedNotifications(ids) @@ -210,7 +210,8 @@ func (c *channelItems) add(ch domain.ChannelChoice, items ...notifyItem) { } // deliver sends the items to the user's mail address, Discord webhook and/or own webhook. -func (s *Server) deliver(userID, heading string, out channelItems, summary bool) error { +// now is the time the run is for (greeting "Guten Morgen" follows the summary time, not the clock). +func (s *Server) deliver(userID, heading string, out channelItems, summary bool, now time.Time) error { emailItems, discordItems := out.email, out.discord u, err := s.store.GetUserByID(userID) if err != nil || u == nil || !u.IsActive { @@ -226,9 +227,9 @@ func (s *Server) deliver(userID, heading string, out channelItems, summary bool) if len(emailItems) == 1 { subject = "MindSift: " + emailItems[0].text } - c := s.digestMail(u.DisplayName, emailItems) + c := s.digestMail(u.DisplayName, emailItems, now) if summary { - c = s.summaryMail(u.DisplayName, emailItems) + c = s.summaryMail(u.DisplayName, emailItems, now) } text, htmlBody := renderMail(c) if err := s.sendNotifyEmail(to, subject, text, htmlBody); err != nil { @@ -368,7 +369,7 @@ func (s *Server) sendDiscordDigest(webhookURL, heading string, items []notifyIte } // digestMail: what happened since the last mail (shares, completed shared tasks, watched tasks). -func (s *Server) digestMail(name string, items []notifyItem) mailContent { +func (s *Server) digestMail(name string, items []notifyItem, now time.Time) mailContent { entries := make([]mailItem, 0, len(items)) for _, it := range items { entries = append(entries, mailItem{Title: it.text}) @@ -379,7 +380,7 @@ func (s *Server) digestMail(name string, items []notifyItem) mailContent { } c := mailContent{ Preheader: items[0].text, - Greeting: mailGreeting(time.Now(), name), + Greeting: mailGreeting(now, name), Intro: intro, Sections: []mailSection{{Items: entries}}, Footer: mailFooterAlarms, @@ -391,7 +392,7 @@ func (s *Server) digestMail(name string, items []notifyItem) mailContent { } // summaryMail: the morning overview, grouped into overdue / today / tomorrow. -func (s *Server) summaryMail(name string, items []notifyItem) mailContent { +func (s *Server) summaryMail(name string, items []notifyItem, now time.Time) mailContent { groups := []struct{ key, title, tone string }{ {"overdue", "Überfällig", "danger"}, {"today", "Heute", ""}, @@ -437,7 +438,7 @@ func (s *Server) summaryMail(name string, items []notifyItem) mailContent { } c := mailContent{ Preheader: "Deine Aufgaben: " + strings.Join(parts, ", "), - Greeting: mailGreeting(time.Now(), name), + Greeting: mailGreeting(now, name), Intro: "Hier ist dein Überblick für heute – " + strings.Join(parts, ", ") + ".", Sections: sections, Outro: outro, @@ -471,7 +472,7 @@ func (s *Server) sendDailySummaries(now time.Time) { } var out channelItems out.add(domain.ChannelChoice{Email: ds.Email, Discord: ds.Discord, Webhook: ds.Webhook, Push: ds.Push}, items...) - if err := s.deliver(uid, "Deine Aufgaben für heute", out, true); err != nil { + if err := s.deliver(uid, "Deine Aufgaben für heute", out, true, now); err != nil { slog.Warn("Tageszusammenfassung konnte nicht verschickt werden", "user_id", uid, "error", err) } }