areweplaying/pkg/email/fetch.go
Harry Culpan e6e8d255bd Return IMAP search error instead of swallowing it
A search failure was logged but execution continued, silently returning
'no emails' to the caller. This masked the most likely cause of the
polling not working. Error now propagates so the caller can log and
handle it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 18:34:27 -04:00

112 lines
2.4 KiB
Go

package email
import (
"fmt"
"io"
"net/mail"
"strings"
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
)
func FetchEmails() ([]Email, error) {
config, err := NewConfigFromEnv()
if err != nil {
return nil, err
}
imapClient, err := config.ConnectToServer(IMAP_SERVICE)
if err != nil {
return nil, err
}
defer imapClient.Logout()
return fetchEmails(imapClient)
}
func concatAddresses(addresses []*imap.Address) string {
result := []string{}
for _, address := range addresses {
if len(address.PersonalName) > 0 {
result = append(result, fmt.Sprintf("%s <%s>", address.PersonalName, address.Address()))
} else {
result = append(result, address.Address())
}
}
return strings.Join(result, "; ")
}
func fetchEmails(imapClient *client.Client) ([]Email, error) {
result := []Email{}
// Select the mailbox you want to read
_, err := imapClient.Select("INBOX", false)
if err != nil {
return nil, err
}
criteria := imap.NewSearchCriteria()
criteria.WithoutFlags = []string{imap.SeenFlag}
uids, err := imapClient.Search(criteria)
if err != nil {
return nil, fmt.Errorf("search error: %w", err)
}
if len(uids) > 0 {
// Define the range of emails to fetch
seqSet := new(imap.SeqSet)
seqSet.AddNum(uids...)
// Fetch the required message attributes
messages := make(chan *imap.Message, 10)
fetchErr := make(chan error, 1)
section := &imap.BodySectionName{}
items := []imap.FetchItem{section.FetchItem(), imap.FetchEnvelope}
go func() {
fetchErr <- imapClient.Fetch(seqSet, items, messages)
}()
// Drain the channel on early return so the goroutine can always exit.
defer func() {
for range messages {
}
}()
for msg := range messages {
if len(msg.Envelope.From) == 0 {
continue
}
toAddress := concatAddresses(msg.Envelope.To)
fromAddress := msg.Envelope.From[0].Address()
r := msg.GetBody(section)
if r == nil {
return result, fmt.Errorf("server didn't returned message body")
}
m, err := mail.ReadMessage(r)
if err != nil {
return result, err
}
body, err := io.ReadAll(m.Body)
email := NewEmail(
[]string{toAddress},
[]string{},
fromAddress,
msg.Envelope.Subject,
string(body),
)
result = append(result, *email)
}
if err := <-fetchErr; err != nil {
return result, fmt.Errorf("fetch error: %w", err)
}
}
return result, nil
}