From 2005d7521ad20078f6de7188b08fdabefafe6836 Mon Sep 17 00:00:00 2001 From: James Date: Wed, 18 Feb 2026 04:03:00 -0500 Subject: [PATCH] feat: auto-remove Fully email alerts when email deleted/read in Outlook --- connector_m365.go | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/connector_m365.go b/connector_m365.go index 1cff7e7..cd2ab0b 100644 --- a/connector_m365.go +++ b/connector_m365.go @@ -62,6 +62,7 @@ type m365State struct { LastEmailIDs []string `json:"last_email_ids"` LastTeamsMsgs map[string]string `json:"last_teams_messages"` LastCalendarHash string `json:"last_calendar_hash"` + EmailAlerts map[string]string `json:"email_alerts"` // msgID → Fully alertID } type m365TokenFile struct { @@ -612,6 +613,24 @@ func (c *M365Connector) pollEmail() bool { } } + // Build current unread set for reconciliation + currentSet := make(map[string]bool) + for _, msg := range result.Value { + currentSet[msg.ID] = true + } + + // Auto-remove Fully alerts for emails no longer in unread inbox (deleted or read) + if c.state.EmailAlerts == nil { + c.state.EmailAlerts = make(map[string]string) + } + for msgID, alertID := range c.state.EmailAlerts { + if !currentSet[msgID] { + log.Printf("[m365] Email %s gone from inbox, removing Fully alert %s", msgID[:16], alertID) + removeFullyAlert(alertID) + delete(c.state.EmailAlerts, msgID) + } + } + // Update state with current unread IDs newIDs := make([]string, 0, len(result.Value)) for _, msg := range result.Value { @@ -624,11 +643,15 @@ func (c *M365Connector) pollEmail() bool { summary := fmt.Sprintf("%d new unread email(s): %s", len(newEmails), strings.Join(summaries, "; ")) log.Printf("[m365] %s", summary) c.sendM365WebhookRich("email", summary, details) - // Post AI-summarized alerts to Fully per email (grouped by sender) - for _, d := range details { + // Post AI-summarized alerts to Fully per email; track alert IDs for auto-removal + for i, d := range details { msg := summarizeM365("📧", d.From, d.Subject, d.BodyPreview) - postFullyAlert(msg, "info", "email:"+d.From) + alertID := postFullyAlert(msg, "info", "email:"+d.From) + if alertID != "" && i < len(newEmails) { + c.state.EmailAlerts[newEmails[i]] = alertID + } } + c.saveState() return true }