imap-notify/main.go

155 lines
3.8 KiB
Go
Raw Permalink Normal View History

2025-06-23 00:48:54 +02:00
package main
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"os"
"time"
2025-06-23 10:04:12 +02:00
"io"
"strings"
2025-06-23 00:48:54 +02:00
"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(
2025-06-23 10:04:12 +02:00
"Von: %s\nAntwort an: %s",
from, replyTo,
2025-06-23 00:48:54 +02:00
)
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")
2025-06-23 10:04:12 +02:00
// Action-Button für Antworten
2025-06-23 00:48:54 +02:00
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")
}
}