package main import ( "fmt" "time" imap "github.com/BrianLeishman/go-imap" ) type ReceivedEmail struct { UID uint32 From string FromName string Subject string Date time.Time Preview string Account string AccountID string } func getEmailProviderName(email string) string { if len(email) == 0 { return "Email" } atIdx := -1 for i := len(email) - 1; i >= 0; i-- { if email[i] == '@' { atIdx = i break } } if atIdx == -1 { return "Email" } domain := email[atIdx+1:] for i := len(domain) - 1; i >= 0; i-- { if domain[i] == '.' { return domain[:i] } } return domain } func FetchUnreadEmails(account Account, days int) ([]ReceivedEmail, error) { if account.IMAP.Host == "" { return nil, fmt.Errorf("IMAP host not configured for account: %s", account.Email) } port := account.IMAP.Port if port == 0 { port = 993 } username := account.IMAP.Username if username == "" { username = account.Email } m, err := imap.New(username, account.IMAP.Password, account.IMAP.Host, port) if err != nil { return nil, fmt.Errorf("failed to connect to IMAP server: %w", err) } defer m.Close() if account.CheckID != nil && *account.CheckID { info := ProjectConfig.Info idCmd := fmt.Sprintf("ID (\"name\" \"%s\" \"version\" \"%s\" \"vendor\" \"%s\")", info.Name, info.Version, info.Vendor) _, err := m.Exec(idCmd, false, 0, func(line []byte) error { return nil }) if err != nil { fmt.Printf("WARNING: failed to send IMAP ID: %v\n", err) } m.Exec("NOOP", false, 0, nil) } err = m.SelectFolder("INBOX") if err != nil { return nil, fmt.Errorf("failed to select inbox: %w", err) } sinceDate := time.Now().AddDate(0, 0, -days) uids, err := m.GetUIDs(imap.Search().Since(sinceDate).Unseen().Build()) if err != nil { return nil, fmt.Errorf("failed to search emails: %w", err) } if len(uids) == 0 { return nil, nil } overviews, err := m.GetOverviews(uids...) if err != nil { return nil, fmt.Errorf("failed to fetch emails: %w", err) } var emails []ReceivedEmail accountName := getEmailProviderName(account.Email) accountID := account.Email if account.Name != "" { accountName = account.Name accountID = account.Name } for uid, email := range overviews { from := "" if len(email.From) > 0 { for _, addr := range email.From { from = addr break } } emails = append(emails, ReceivedEmail{ UID: uint32(uid), From: from, FromName: "", Subject: email.Subject, Date: email.Sent, Preview: fmt.Sprintf("(%.1f KB)", float64(email.Size)/1024), Account: accountName, AccountID: accountID, }) } return emails, nil } func GetAccountByEmail(email string) (Account, error) { accounts, err := getAccounts() if err != nil { return Account{}, err } for _, acc := range accounts { if acc.Email == email || acc.Name == email { return acc, nil } } return Account{}, fmt.Errorf("account not found: %s", email) } func GetAccountByID(id string) (Account, error) { accounts, err := getAccounts() if err != nil { return Account{}, err } for _, acc := range accounts { if acc.Email == id || acc.Name == id { return acc, nil } } return Account{}, fmt.Errorf("account not found: %s", id) } func FetchAllUnreadEmails(days int) ([]ReceivedEmail, error) { accounts, err := getAccounts() if err != nil { return nil, err } if len(accounts) == 0 { return nil, fmt.Errorf("no accounts configured") } var allEmails []ReceivedEmail for _, account := range accounts { emails, err := FetchUnreadEmails(account, days) if err != nil { continue } allEmails = append(allEmails, emails...) } sortEmailsByDate(allEmails) return allEmails, nil } func sortEmailsByDate(emails []ReceivedEmail) { for i := 0; i < len(emails)-1; i++ { for j := i + 1; j < len(emails); j++ { if emails[j].Date.After(emails[i].Date) { emails[i], emails[j] = emails[j], emails[i] } } } }