202 lines
4.8 KiB
Go
202 lines
4.8 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/emersion/go-imap"
|
|
"github.com/emersion/go-imap/client"
|
|
"github.com/emersion/go-message/mail"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
var bodyText string
|
|
if r := msg.GetBody(section); r != nil {
|
|
bodyBytes, err := io.ReadAll(r)
|
|
if err != nil {
|
|
log.Printf("Fehler beim Lesen des Bodys: %v", err)
|
|
} else {
|
|
bodyText = extractTextFromBody(bodyBytes)
|
|
if len(bodyText) > 200 {
|
|
bodyText = bodyText[:200] + "..."
|
|
}
|
|
}
|
|
}
|
|
|
|
message := fmt.Sprintf(
|
|
"Betreff: %s\nVon: %s\nAntwort an: %s\n\n%s",
|
|
subject, from, replyTo, bodyText,
|
|
)
|
|
|
|
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 extractTextFromBody(bodyBytes []byte) string {
|
|
r := strings.NewReader(string(bodyBytes))
|
|
mr, err := mail.CreateReader(r)
|
|
if err != nil {
|
|
return string(bodyBytes)
|
|
}
|
|
|
|
var textContent strings.Builder
|
|
|
|
for {
|
|
p, err := mr.NextPart()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
switch h := p.Header.(type) {
|
|
case *mail.InlineHeader:
|
|
contentType, _, _ := h.ContentType()
|
|
if contentType == "text/plain" {
|
|
partBytes, _ := io.ReadAll(p.Body)
|
|
textContent.Write(partBytes)
|
|
}
|
|
}
|
|
}
|
|
|
|
if textContent.Len() > 0 {
|
|
return textContent.String()
|
|
}
|
|
return string(bodyBytes)
|
|
}
|
|
|
|
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")
|
|
|
|
if replyTo != "" {
|
|
action := fmt.Sprintf("view, Antworten, mailto:%s", replyTo)
|
|
req.Header.Set("Actions", action)
|
|
log.Printf("Füge Action-Button hinzu: %s", 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")
|
|
}
|
|
}
|