sdd
This commit is contained in:
253
pkg/api/email_fetch.go
Normal file
253
pkg/api/email_fetch.go
Normal file
@@ -0,0 +1,253 @@
|
||||
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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user