package main import ( "crypto/tls" "fmt" "log" "net/http" "os" "time" "io" "strings" "github.com/emersion/go-imap" "github.com/emersion/go-imap/client" ) func main() { log.SetOutput(os.Stdout) log.Println("IMAP-ntfy Notification Service gestartet") imapServer := os.Getenv("IMAP_SERVER") imapPort := os.Getenv("IMAP_PORT") emailUser := os.Getenv("EMAIL_USER") emailPass := os.Getenv("EMAIL_PASSWORD") ntfyServer := os.Getenv("NTFY_SERVER") ntfyTopic := os.Getenv("NTFY_TOPIC") ntfyUser := os.Getenv("NTFY_USER") ntfyPass := os.Getenv("NTFY_PASSWORD") if ntfyServer == "" { ntfyServer = "https://ntfy.sh" log.Println("NTFY_SERVER nicht gesetzt, verwende Standardwert:", ntfyServer) } for { log.Println("Starte IMAP-Abfrage...") addr := fmt.Sprintf("%s:%s", imapServer, imapPort) c, err := client.DialTLS(addr, &tls.Config{InsecureSkipVerify: true}) if err != nil { log.Printf("IMAP-Verbindungsfehler: %v", err) time.Sleep(1 * time.Minute) continue } defer c.Logout() if err := c.Login(emailUser, emailPass); err != nil { log.Printf("IMAP-Login fehlgeschlagen: %v", err) time.Sleep(1 * time.Minute) continue } _, err = c.Select("INBOX", false) if err != nil { log.Printf("Postfachauswahl fehlgeschlagen: %v", err) time.Sleep(1 * time.Minute) continue } criteria := imap.NewSearchCriteria() criteria.WithoutFlags = []string{"\\Seen"} ids, err := c.Search(criteria) if err != nil { log.Printf("Suche fehlgeschlagen: %v", err) time.Sleep(1 * time.Minute) continue } if len(ids) > 0 { log.Printf("Gefunden: %d neue Nachrichten", len(ids)) seqset := new(imap.SeqSet) seqset.AddNum(ids...) section := &imap.BodySectionName{} items := []imap.FetchItem{imap.FetchEnvelope, section.FetchItem()} messages := make(chan *imap.Message, 10) go func() { if err := c.Fetch(seqset, items, messages); err != nil { log.Printf("Fetch fehlgeschlagen: %v", err) } }() for msg := range messages { subject := msg.Envelope.Subject from := "" if len(msg.Envelope.From) > 0 { from = msg.Envelope.From[0].Address() } replyTo := "" if len(msg.Envelope.ReplyTo) > 0 { replyTo = msg.Envelope.ReplyTo[0].Address() } else if from != "" { replyTo = from } message := fmt.Sprintf( "Von: %s\nAntwort an: %s", from, replyTo, ) sendNtfyNotification(ntfyServer, ntfyTopic, ntfyUser, ntfyPass, subject, message, replyTo) } item := imap.FormatFlagsOp(imap.AddFlags, true) flags := []interface{}{imap.SeenFlag} if err := c.Store(seqset, item, flags, nil); err != nil { log.Printf("Markieren fehlgeschlagen: %v", err) } } else { log.Println("Keine neuen Nachrichten gefunden.") } c.Logout() log.Println("Warte 1 Minute bis zur nächsten Abfrage...") time.Sleep(1 * time.Minute) } } func sendNtfyNotification(server, topic, user, pass, title, message, replyTo string) { url := fmt.Sprintf("%s/%s", server, topic) log.Printf("Sende ntfy-Benachrichtigung an %s", url) req, _ := http.NewRequest("POST", url, strings.NewReader(message)) req.Header.Set("Title", title) req.Header.Set("Content-Type", "text/plain") // Action-Button für Antworten if replyTo != "" { action := fmt.Sprintf("view, Antworten, mailto:%s", replyTo) req.Header.Set("Actions", action) } if user != "" && pass != "" { req.SetBasicAuth(user, pass) } resp, err := http.DefaultClient.Do(req) if err != nil { log.Printf("ntfy-Fehler: %v", err) return } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) log.Printf("ntfy-Fehlerstatus: %d", resp.StatusCode) log.Printf("Fehlermeldung: %s", string(body)) } else { log.Println("ntfy-Benachrichtigung erfolgreich gesendet") } }