import ( "encoding/json" "fmt" "io" "log" "os" "strings" "time" "github.com/emersion/go-imap" "github.com/emersion/go-imap/client" "github.com/emersion/go-imap-idle" "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) }