162 lines
3.7 KiB
Go
162 lines
3.7 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/emersion/go-imap"
|
|
idle "github.com/emersion/go-imap-idle"
|
|
"github.com/emersion/go-imap/client"
|
|
"github.com/emersion/go-message/mail"
|
|
)
|
|
|
|
func main() {
|
|
host := os.Getenv("IMAP_HOST")
|
|
username := os.Getenv("IMAP_USERNAME")
|
|
password := os.Getenv("IMAP_PASSWORD")
|
|
folder := os.Getenv("IMAP_FOLDER")
|
|
if folder == "" {
|
|
folder = "INBOX"
|
|
}
|
|
|
|
if host == "" || username == "" || password == "" {
|
|
log.Fatal("Required environment variables are not set")
|
|
}
|
|
|
|
log.Println("Connecting to server...")
|
|
c, err := client.DialTLS(host, nil)
|
|
if err != nil {
|
|
log.Fatalf("Failed to connect to IMAP server: %v", err)
|
|
}
|
|
defer c.Logout()
|
|
|
|
if err := c.Login(username, password); err != nil {
|
|
log.Fatalf("Failed to log in: %v", err)
|
|
}
|
|
|
|
log.Println("Logged in and authenticated")
|
|
|
|
// Ensure the selected folder is continuously monitored
|
|
for {
|
|
if err := monitorFolder(c, folder); err != nil {
|
|
log.Printf("Error monitoring folder %s: %v", folder, err)
|
|
log.Println("Attempting to reconnect in 30 seconds...")
|
|
time.Sleep(30 * time.Second)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func monitorFolder(c *client.Client, folder string) error {
|
|
_, err := c.Select(folder, false)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to select '%s': %w", folder, err)
|
|
}
|
|
|
|
log.Printf("Monitoring folder: %s", folder)
|
|
idleClient := idle.NewClient(c)
|
|
done := make(chan error, 1)
|
|
|
|
go func() {
|
|
done <- idleClient.Idle(nil)
|
|
}()
|
|
|
|
for {
|
|
select {
|
|
case err := <-done:
|
|
if err != nil {
|
|
return fmt.Errorf("idle error: %w", err)
|
|
}
|
|
return nil
|
|
case <-time.After(29 * time.Minute): // Re-initiate IDLE approximately every 29 minutes
|
|
if err := idleClient.Terminate(); err != nil {
|
|
log.Printf("Error terminating IDLE: %v", err)
|
|
}
|
|
return fmt.Errorf("reinitializing IDLE")
|
|
}
|
|
}
|
|
}
|
|
|
|
func processNewMessages(c *client.Client, mbox *imap.MailboxStatus) {
|
|
seqset := new(imap.SeqSet)
|
|
seqset.AddRange(mbox.Messages, mbox.Messages)
|
|
|
|
messages := make(chan *imap.Message, 10)
|
|
go func() {
|
|
if err := c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope, imap.FetchBody}, messages); err != nil {
|
|
log.Fatalf("Failed to fetch new messages: %v", err)
|
|
}
|
|
}()
|
|
|
|
for msg := range messages {
|
|
processMessage(msg)
|
|
}
|
|
}
|
|
|
|
// processMessage processes a single message, extracting data and saving to JSON
|
|
func processMessage(msg *imap.Message) {
|
|
if msg == nil {
|
|
log.Println("No message to process")
|
|
return
|
|
}
|
|
|
|
body := msg.GetBody(&imap.BodySectionName{})
|
|
if body == nil {
|
|
log.Println("No body fetched for this message")
|
|
return
|
|
}
|
|
|
|
r, err := mail.CreateReader(body)
|
|
if err != nil {
|
|
log.Fatalf("Failed to create mail reader: %v", err)
|
|
}
|
|
|
|
var emailBody strings.Builder
|
|
for {
|
|
p, err := r.NextPart()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
log.Fatalf("Failed to read part: %v", err)
|
|
}
|
|
|
|
switch h := p.Header.(type) {
|
|
case *mail.InlineHeader:
|
|
_, params, _ := h.ContentType()
|
|
if params["charset"] != "" {
|
|
_, err = io.Copy(&emailBody, p.Body)
|
|
if err != nil {
|
|
log.Fatalf("Failed to read body: %v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
emailData := map[string]interface{}{
|
|
"subject": msg.Envelope.Subject,
|
|
"from": msg.Envelope.From,
|
|
"to": msg.Envelope.To,
|
|
"date": msg.Envelope.Date,
|
|
"body": emailBody.String(),
|
|
}
|
|
|
|
emailJSON, err := json.MarshalIndent(emailData, "", " ")
|
|
if err != nil {
|
|
log.Fatalf("Failed to marshal JSON: %v", err)
|
|
}
|
|
|
|
timestamp := time.Now().Format("20060102-150405")
|
|
fileName := fmt.Sprintf("email-%s.json", timestamp)
|
|
err = os.WriteFile(fileName, emailJSON, 0644)
|
|
if err != nil {
|
|
log.Fatalf("Failed to write file %s: %v", fileName, err)
|
|
}
|
|
log.Printf("Saved %s", fileName)
|
|
}
|