package api import ( "fmt" "io" "io/ioutil" "strings" "time" "github.com/emersion/go-imap" "github.com/emersion/go-imap/client" "github.com/emersion/go-message/mail" ) // EmailMessage 表示一封邮件 type EmailMessage struct { ID string Subject string From string To string Date time.Time Body string MimeType string } // GetLatestEmails 获取指定邮箱的最新邮件 func GetLatestEmails(emailAddr, password, server string, port int, useSSL bool, numEmails int) ([]*EmailMessage, error) { fmt.Printf("正在连接到 %s:%d...\n", server, port) // 连接到服务器 var c *client.Client var err error if useSSL { c, err = client.DialTLS(fmt.Sprintf("%s:%d", server, port), nil) } else { c, err = client.Dial(fmt.Sprintf("%s:%d", server, port)) } if err != nil { return nil, fmt.Errorf("连接到IMAP服务器失败: %w", err) } defer c.Logout() // 登录 fmt.Printf("正在登录 %s...\n", emailAddr) if err := c.Login(emailAddr, password); err != nil { return nil, fmt.Errorf("登录失败: %w", err) } // 选择收件箱 mbox, err := c.Select("INBOX", false) if err != nil { return nil, fmt.Errorf("选择收件箱失败: %w", err) } if mbox.Messages == 0 { fmt.Println("收件箱为空") return nil, nil } // 计算要获取的消息范围 from := uint32(1) if mbox.Messages > uint32(numEmails) { from = mbox.Messages - uint32(numEmails) + 1 } to := mbox.Messages // 创建搜索条件 criteria := imap.NewSearchCriteria() criteria.WithoutFlags = []string{imap.DeletedFlag} if from > 1 { criteria.Uid = new(imap.SeqSet) criteria.Uid.AddRange(from, to) } // 搜索消息 ids, err := c.Search(criteria) if err != nil { return nil, fmt.Errorf("搜索邮件失败: %w", err) } if len(ids) == 0 { fmt.Println("未找到符合条件的邮件") return nil, nil } // 限制获取的邮件数量 if len(ids) > numEmails { ids = ids[len(ids)-numEmails:] } // 创建序列集 seqSet := new(imap.SeqSet) seqSet.AddNum(ids...) // 设置获取项 items := []imap.FetchItem{imap.FetchEnvelope, imap.FetchBodyStructure, imap.FetchFlags, imap.FetchRFC822Text, imap.FetchUid} // 获取消息 messages := make(chan *imap.Message, 10) done := make(chan error, 1) go func() { done <- c.Fetch(seqSet, items, messages) }() // 处理消息 var emails []*EmailMessage for msg := range messages { email := &EmailMessage{ ID: fmt.Sprintf("%d", msg.Uid), } if msg.Envelope != nil { email.Subject = msg.Envelope.Subject if len(msg.Envelope.From) > 0 { email.From = formatAddress(msg.Envelope.From[0]) } if len(msg.Envelope.To) > 0 { email.To = formatAddress(msg.Envelope.To[0]) } email.Date = msg.Envelope.Date } // 获取正文 var section imap.BodySectionName section.Specifier = imap.TextSpecifier r := msg.GetBody(§ion) if r == nil { // 尝试获取全部内容 section = imap.BodySectionName{} r = msg.GetBody(§ion) if r == nil { continue } } // 处理正文 body, err := extractBody(r) if err != nil { fmt.Printf("提取邮件正文失败: %v\n", err) continue } email.Body = body emails = append(emails, email) fmt.Printf("已获取邮件ID: %s\n", email.ID) } if err := <-done; err != nil { return nil, fmt.Errorf("获取邮件失败: %w", err) } // 反转列表,让最新的邮件在前面 for i, j := 0, len(emails)-1; i < j; i, j = i+1, j-1 { emails[i], emails[j] = emails[j], emails[i] } return emails, nil } // formatAddress 格式化地址 func formatAddress(addr *imap.Address) string { if addr == nil { return "" } if addr.PersonalName != "" { return fmt.Sprintf("%s <%s@%s>", addr.PersonalName, addr.MailboxName, addr.HostName) } return fmt.Sprintf("%s@%s", addr.MailboxName, addr.HostName) } // extractBody 提取邮件正文 func extractBody(r io.Reader) (string, error) { if r == nil { return "", fmt.Errorf("无邮件内容") } // 尝试解析为邮件 mr, err := mail.CreateReader(r) if err != nil { // 如果无法解析为邮件,直接读取内容 data, err := ioutil.ReadAll(r) if err != nil { return "", fmt.Errorf("读取邮件内容失败: %w", err) } return string(data), nil } var textParts []string for { p, err := mr.NextPart() if err == io.EOF { break } if err != nil { return "", fmt.Errorf("读取邮件部分失败: %w", err) } switch h := p.Header.(type) { case *mail.InlineHeader: contentType, _, _ := h.ContentType() if strings.HasPrefix(contentType, "text/plain") { data, err := ioutil.ReadAll(p.Body) if err != nil { return "", fmt.Errorf("读取文本部分失败: %w", err) } textParts = append(textParts, string(data)) } } } if len(textParts) > 0 { return strings.Join(textParts, "\n"), nil } // 如果没有找到文本部分,尝试读取原始内容 data, err := ioutil.ReadAll(r) if err != nil { return "", fmt.Errorf("读取原始内容失败: %w", err) } return string(data), nil } // PrintEmails 打印邮件信息 func PrintEmails(emails []*EmailMessage) { if len(emails) == 0 { fmt.Println("没有找到邮件") return } for i, email := range emails { fmt.Println("\n" + strings.Repeat("=", 60)) fmt.Printf("邮件 %d/%d\n", i+1, len(emails)) fmt.Println(strings.Repeat("-", 60)) fmt.Printf("主题: %s\n", email.Subject) fmt.Printf("发件人: %s\n", email.From) fmt.Printf("收件人: %s\n", email.To) fmt.Printf("日期: %s\n", email.Date.Format(time.RFC1123Z)) fmt.Println(strings.Repeat("-", 60)) fmt.Println("邮件正文:") body := email.Body if len(body) > 1000 { body = body[:1000] + "..." } fmt.Println(body) fmt.Println(strings.Repeat("=", 60)) } }