Compare commits
	
		
			No commits in common. "main" and "v0.0.1" have entirely different histories.
		
	
	
		
	
		
							
								
								
									
										42
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								go.mod
									
									
									
									
									
								
							@ -1,38 +1,34 @@
 | 
				
			|||||||
module apigo.cc/gojs/mail
 | 
					module apigo.cc/gojs/mail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
go 1.24
 | 
					go 1.18
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	apigo.cc/gojs v0.0.25
 | 
						apigo.cc/gojs v0.0.12
 | 
				
			||||||
	apigo.cc/gojs/console v0.0.2
 | 
						apigo.cc/gojs/console v0.0.2
 | 
				
			||||||
	github.com/emersion/go-imap/v2 v2.0.0-beta.5
 | 
						apigo.cc/gojs/redis v0.0.2
 | 
				
			||||||
	github.com/emersion/go-message v0.18.1
 | 
						github.com/ssgo/u v1.7.13
 | 
				
			||||||
	github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056
 | 
					 | 
				
			||||||
	github.com/ssgo/config v1.7.9
 | 
					 | 
				
			||||||
	github.com/ssgo/u v1.7.21
 | 
					 | 
				
			||||||
	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
require (
 | 
					require (
 | 
				
			||||||
	github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
 | 
						github.com/dlclark/regexp2 v1.11.4 // indirect
 | 
				
			||||||
	github.com/dlclark/regexp2 v1.11.5 // indirect
 | 
						github.com/emersion/go-imap v1.2.1 // indirect
 | 
				
			||||||
	github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect
 | 
						github.com/fsnotify/fsnotify v1.8.0 // indirect
 | 
				
			||||||
	github.com/fsnotify/fsnotify v1.9.0 // indirect
 | 
					 | 
				
			||||||
	github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
 | 
						github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
 | 
				
			||||||
	github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
 | 
						github.com/gomodule/redigo v1.9.2 // indirect
 | 
				
			||||||
	github.com/google/pprof v0.0.0-20250630185457-6e76a2b096b5 // indirect
 | 
						github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
 | 
				
			||||||
	github.com/jhillyerd/enmime v1.3.0 // indirect
 | 
						github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
 | 
				
			||||||
	github.com/mattn/go-runewidth v0.0.15 // indirect
 | 
						github.com/mattn/go-runewidth v0.0.9 // indirect
 | 
				
			||||||
	github.com/olekukonko/tablewriter v0.0.5 // indirect
 | 
						github.com/olekukonko/tablewriter v0.0.5 // indirect
 | 
				
			||||||
	github.com/pkg/errors v0.9.1 // indirect
 | 
						github.com/ssgo/config v1.7.9 // indirect
 | 
				
			||||||
	github.com/rivo/uniseg v0.4.4 // indirect
 | 
						github.com/ssgo/log v1.7.7 // indirect
 | 
				
			||||||
	github.com/ssgo/log v1.7.9 // indirect
 | 
						github.com/ssgo/redis v1.7.7 // indirect
 | 
				
			||||||
	github.com/ssgo/standard v1.7.7 // indirect
 | 
						github.com/ssgo/standard v1.7.7 // indirect
 | 
				
			||||||
	github.com/ssgo/tool v0.4.29 // indirect
 | 
						github.com/ssgo/tool v0.4.28 // indirect
 | 
				
			||||||
	github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
 | 
						github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
 | 
				
			||||||
	golang.org/x/net v0.42.0 // indirect
 | 
						golang.org/x/net v0.32.0 // indirect
 | 
				
			||||||
	golang.org/x/sys v0.34.0 // indirect
 | 
						golang.org/x/sys v0.28.0 // indirect
 | 
				
			||||||
	golang.org/x/text v0.27.0 // indirect
 | 
						golang.org/x/text v0.21.0 // indirect
 | 
				
			||||||
	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 | 
						gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 | 
				
			||||||
 | 
						gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
 | 
				
			||||||
	gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
						gopkg.in/yaml.v3 v3.0.1 // indirect
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										370
									
								
								imap.go
									
									
									
									
									
								
							
							
						
						
									
										370
									
								
								imap.go
									
									
									
									
									
								
							@ -1,370 +0,0 @@
 | 
				
			|||||||
package mail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"bytes"
 | 
					 | 
				
			||||||
	"fmt"
 | 
					 | 
				
			||||||
	"math"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"apigo.cc/gojs"
 | 
					 | 
				
			||||||
	"apigo.cc/gojs/goja"
 | 
					 | 
				
			||||||
	"github.com/emersion/go-imap/v2"
 | 
					 | 
				
			||||||
	"github.com/emersion/go-imap/v2/imapclient"
 | 
					 | 
				
			||||||
	"github.com/jhillyerd/enmime"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Mail struct {
 | 
					 | 
				
			||||||
	Uid         uint32
 | 
					 | 
				
			||||||
	From        []string
 | 
					 | 
				
			||||||
	To          []string
 | 
					 | 
				
			||||||
	Cc          []string
 | 
					 | 
				
			||||||
	Bcc         []string
 | 
					 | 
				
			||||||
	Subject     string
 | 
					 | 
				
			||||||
	Date        string
 | 
					 | 
				
			||||||
	Text        string
 | 
					 | 
				
			||||||
	Html        string
 | 
					 | 
				
			||||||
	Attachments []Attachment
 | 
					 | 
				
			||||||
	Embeds      []Attachment
 | 
					 | 
				
			||||||
	Size        int64
 | 
					 | 
				
			||||||
	Flags       []string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 接收选项
 | 
					 | 
				
			||||||
type RecvOption struct {
 | 
					 | 
				
			||||||
	Tag        uint32
 | 
					 | 
				
			||||||
	Mailbox    string
 | 
					 | 
				
			||||||
	Unseen     bool
 | 
					 | 
				
			||||||
	Read       bool
 | 
					 | 
				
			||||||
	Answered   bool
 | 
					 | 
				
			||||||
	Flagged    bool
 | 
					 | 
				
			||||||
	Deleted    bool
 | 
					 | 
				
			||||||
	Since      string
 | 
					 | 
				
			||||||
	Before     string
 | 
					 | 
				
			||||||
	Limit      uint32
 | 
					 | 
				
			||||||
	MarkAsRead bool
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	Subject []string
 | 
					 | 
				
			||||||
	Search  []string
 | 
					 | 
				
			||||||
	Body    []string
 | 
					 | 
				
			||||||
	From    []string
 | 
					 | 
				
			||||||
	To      []string
 | 
					 | 
				
			||||||
	Cc      []string
 | 
					 | 
				
			||||||
	Bcc     []string
 | 
					 | 
				
			||||||
	Not     []string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type MailboxInfo struct {
 | 
					 | 
				
			||||||
	Name      string
 | 
					 | 
				
			||||||
	Delimiter string
 | 
					 | 
				
			||||||
	Attrs     []string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type RecvResult struct {
 | 
					 | 
				
			||||||
	List []Mail
 | 
					 | 
				
			||||||
	Tag  uint32
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type IMAPClient struct {
 | 
					 | 
				
			||||||
	client *imapclient.Client
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *IMAPClient) Close(vm *goja.Runtime) {
 | 
					 | 
				
			||||||
	obj.client.Logout().Wait()
 | 
					 | 
				
			||||||
	obj.client.Close()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *Mailbox) OpenIMAP(vm *goja.Runtime) (*IMAPClient, error) {
 | 
					 | 
				
			||||||
	logger := gojs.GetLogger(vm)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 连接服务器
 | 
					 | 
				
			||||||
	addr := fmt.Sprintf("%s:%d", obj.imapHost, obj.imapPort)
 | 
					 | 
				
			||||||
	imapClient, err := imapclient.DialTLS(addr, nil)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("failed to connect to IMAP server", "addr", addr, "err", err)
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 发送客户端标识
 | 
					 | 
				
			||||||
	imapClient.ID(&imap.IDData{
 | 
					 | 
				
			||||||
		Name:       conf.ClientName,
 | 
					 | 
				
			||||||
		Version:    conf.ClientVersion,
 | 
					 | 
				
			||||||
		Vendor:     conf.ClientVendor,
 | 
					 | 
				
			||||||
		SupportURL: conf.ClientSupportURL,
 | 
					 | 
				
			||||||
		OS:         conf.ClientOS,
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 登录
 | 
					 | 
				
			||||||
	if err := imapClient.Login(obj.username, obj.password).Wait(); err != nil {
 | 
					 | 
				
			||||||
		logger.Error("IMAP login failed", "user", obj.username, "err", err)
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &IMAPClient{client: imapClient}, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *IMAPClient) ListMailbox(vm *goja.Runtime) ([]MailboxInfo, error) {
 | 
					 | 
				
			||||||
	logger := gojs.GetLogger(vm)
 | 
					 | 
				
			||||||
	mailboxes, err := obj.client.List("", "%", nil).Collect()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("failed to list mailboxes", "err", err)
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	list := []MailboxInfo{}
 | 
					 | 
				
			||||||
	for _, mbox := range mailboxes {
 | 
					 | 
				
			||||||
		// fmt.Println(u.JsonP(mbox), 111)
 | 
					 | 
				
			||||||
		attrs := []string{}
 | 
					 | 
				
			||||||
		for _, attr := range mbox.Attrs {
 | 
					 | 
				
			||||||
			attrs = append(attrs, string(attr))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		list = append(list, MailboxInfo{
 | 
					 | 
				
			||||||
			Name:      mbox.Mailbox,
 | 
					 | 
				
			||||||
			Delimiter: string(mbox.Delim),
 | 
					 | 
				
			||||||
			Attrs:     attrs,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return list, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *IMAPClient) Recv(vm *goja.Runtime, opt *RecvOption) (*RecvResult, error) {
 | 
					 | 
				
			||||||
	logger := gojs.GetLogger(vm)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 创建默认选项
 | 
					 | 
				
			||||||
	if opt == nil {
 | 
					 | 
				
			||||||
		opt = &RecvOption{
 | 
					 | 
				
			||||||
			Mailbox: "INBOX",
 | 
					 | 
				
			||||||
			Unseen:  true,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	rr := &RecvResult{Tag: opt.Tag, List: []Mail{}}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 设置默认限制
 | 
					 | 
				
			||||||
	if opt.Limit == 0 {
 | 
					 | 
				
			||||||
		opt.Limit = 20
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 选择邮箱
 | 
					 | 
				
			||||||
	mailboxName := opt.Mailbox
 | 
					 | 
				
			||||||
	if mailboxName == "" {
 | 
					 | 
				
			||||||
		mailboxName = "INBOX"
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 选择邮箱
 | 
					 | 
				
			||||||
	c := obj.client
 | 
					 | 
				
			||||||
	selectCmd := c.Select(mailboxName, nil)
 | 
					 | 
				
			||||||
	_, err := selectCmd.Wait()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("failed to select mailbox", "mailbox", mailboxName, "err", err)
 | 
					 | 
				
			||||||
		return rr, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	limit := imap.UID(opt.Limit)
 | 
					 | 
				
			||||||
	startUID := imap.UID(opt.Tag) + 1  // 从上一次最大UID+1开始
 | 
					 | 
				
			||||||
	endUID := imap.UID(math.MaxUint32) // 始终使用最大值,确保搜索时能拿到足够的结果
 | 
					 | 
				
			||||||
	criteria := &imap.SearchCriteria{
 | 
					 | 
				
			||||||
		UID: []imap.UIDSet{[]imap.UIDRange{{Start: startUID, Stop: endUID}}},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 添加基本搜索条件
 | 
					 | 
				
			||||||
	if opt.Unseen {
 | 
					 | 
				
			||||||
		criteria.NotFlag = append(criteria.NotFlag, imap.FlagSeen)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opt.Read {
 | 
					 | 
				
			||||||
		criteria.Flag = append(criteria.Flag, imap.FlagSeen)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opt.Answered {
 | 
					 | 
				
			||||||
		criteria.Flag = append(criteria.Flag, imap.FlagAnswered)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opt.Flagged {
 | 
					 | 
				
			||||||
		criteria.Flag = append(criteria.Flag, imap.FlagFlagged)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opt.Deleted {
 | 
					 | 
				
			||||||
		criteria.Flag = append(criteria.Flag, imap.FlagDeleted)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 添加时间范围条件
 | 
					 | 
				
			||||||
	if opt.Since != "" {
 | 
					 | 
				
			||||||
		criteria.Since = u.ParseTime(opt.Since)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if opt.Before != "" {
 | 
					 | 
				
			||||||
		criteria.Before = u.ParseTime(opt.Before)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// 添加自定义搜索条件
 | 
					 | 
				
			||||||
	if len(opt.Search) > 0 {
 | 
					 | 
				
			||||||
		criteria.Text = opt.Search
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(opt.Body) > 0 {
 | 
					 | 
				
			||||||
		criteria.Body = opt.Body
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(opt.Subject) > 0 {
 | 
					 | 
				
			||||||
		for _, word := range opt.Subject {
 | 
					 | 
				
			||||||
			criteria.Header = append(criteria.Header, imap.SearchCriteriaHeaderField{
 | 
					 | 
				
			||||||
				Key:   "Subject",
 | 
					 | 
				
			||||||
				Value: word,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(opt.From) > 0 {
 | 
					 | 
				
			||||||
		for _, word := range opt.From {
 | 
					 | 
				
			||||||
			criteria.Header = append(criteria.Header, imap.SearchCriteriaHeaderField{
 | 
					 | 
				
			||||||
				Key:   "From",
 | 
					 | 
				
			||||||
				Value: word,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(opt.To) > 0 {
 | 
					 | 
				
			||||||
		for _, word := range opt.To {
 | 
					 | 
				
			||||||
			criteria.Header = append(criteria.Header, imap.SearchCriteriaHeaderField{
 | 
					 | 
				
			||||||
				Key:   "To",
 | 
					 | 
				
			||||||
				Value: word,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(opt.Cc) > 0 {
 | 
					 | 
				
			||||||
		for _, word := range opt.Cc {
 | 
					 | 
				
			||||||
			criteria.Header = append(criteria.Header, imap.SearchCriteriaHeaderField{
 | 
					 | 
				
			||||||
				Key:   "Cc",
 | 
					 | 
				
			||||||
				Value: word,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(opt.Bcc) > 0 {
 | 
					 | 
				
			||||||
		for _, word := range opt.Bcc {
 | 
					 | 
				
			||||||
			criteria.Header = append(criteria.Header, imap.SearchCriteriaHeaderField{
 | 
					 | 
				
			||||||
				Key:   "Bcc",
 | 
					 | 
				
			||||||
				Value: word,
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(opt.Not) > 0 {
 | 
					 | 
				
			||||||
		criteria.Not = []imap.SearchCriteria{}
 | 
					 | 
				
			||||||
		criteria.Not = append(criteria.Not, imap.SearchCriteria{
 | 
					 | 
				
			||||||
			Text: opt.Not,
 | 
					 | 
				
			||||||
		})
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	r, err := c.UIDSearch(criteria, nil).Wait()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("failed to search messages", "err", err)
 | 
					 | 
				
			||||||
		return rr, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var uidSet imap.UIDSet
 | 
					 | 
				
			||||||
	maxUID := startUID
 | 
					 | 
				
			||||||
	var uidCount imap.UID
 | 
					 | 
				
			||||||
	if all, ok := r.All.(imap.UIDSet); ok {
 | 
					 | 
				
			||||||
		for _, set := range all {
 | 
					 | 
				
			||||||
			n := set.Stop - set.Start
 | 
					 | 
				
			||||||
			if uidCount+n > limit {
 | 
					 | 
				
			||||||
				set.Stop -= (uidCount + n - limit)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			maxUID = set.Stop
 | 
					 | 
				
			||||||
			uidCount += set.Stop - set.Start
 | 
					 | 
				
			||||||
			uidSet = append(uidSet, set)
 | 
					 | 
				
			||||||
			if uidCount >= limit {
 | 
					 | 
				
			||||||
				break
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if len(uidSet) == 0 {
 | 
					 | 
				
			||||||
		return rr, nil
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	fr, err := c.Fetch(uidSet, &imap.FetchOptions{Envelope: true, BodySection: []*imap.FetchItemBodySection{{}}}).Collect()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("failed to fetch messages", "err", err)
 | 
					 | 
				
			||||||
		return rr, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, msg := range fr {
 | 
					 | 
				
			||||||
		mail := Mail{
 | 
					 | 
				
			||||||
			Uid:     uint32(msg.UID),
 | 
					 | 
				
			||||||
			Subject: msg.Envelope.Subject,
 | 
					 | 
				
			||||||
			From:    FormatAddressList(msg.Envelope.From),
 | 
					 | 
				
			||||||
			To:      FormatAddressList(msg.Envelope.To),
 | 
					 | 
				
			||||||
			Cc:      FormatAddressList(msg.Envelope.Cc),
 | 
					 | 
				
			||||||
			Bcc:     FormatAddressList(msg.Envelope.Bcc),
 | 
					 | 
				
			||||||
			Date:    msg.Envelope.Date.Format(time.DateTime),
 | 
					 | 
				
			||||||
			Size:    msg.RFC822Size,
 | 
					 | 
				
			||||||
			Flags:   FormatFlags(msg.Flags),
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// 解析邮件正文和附件
 | 
					 | 
				
			||||||
		// fmt.Println("####0", len(msg.BodySection))
 | 
					 | 
				
			||||||
		for _, section := range msg.BodySection {
 | 
					 | 
				
			||||||
			if section.Section != nil && section.Section.Specifier == imap.PartSpecifierNone {
 | 
					 | 
				
			||||||
				// fmt.Println("####1", len(section.Bytes))
 | 
					 | 
				
			||||||
				env, err := enmime.ReadEnvelope(bytes.NewReader(section.Bytes))
 | 
					 | 
				
			||||||
				if err == nil {
 | 
					 | 
				
			||||||
					mail.Text = env.Text
 | 
					 | 
				
			||||||
					mail.Html = env.HTML
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// 处理附件
 | 
					 | 
				
			||||||
					for _, attach := range env.Attachments {
 | 
					 | 
				
			||||||
						mail.Attachments = append(mail.Attachments, Attachment{
 | 
					 | 
				
			||||||
							Name:        attach.FileName,
 | 
					 | 
				
			||||||
							Data:        u.Base64(attach.Content),
 | 
					 | 
				
			||||||
							ContentType: attach.ContentType,
 | 
					 | 
				
			||||||
						})
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					// 处理内嵌资源
 | 
					 | 
				
			||||||
					for _, embed := range env.Inlines {
 | 
					 | 
				
			||||||
						mail.Embeds = append(mail.Embeds, Attachment{
 | 
					 | 
				
			||||||
							Name:        embed.FileName,
 | 
					 | 
				
			||||||
							Data:        u.Base64(embed.Content),
 | 
					 | 
				
			||||||
							ContentType: embed.ContentType,
 | 
					 | 
				
			||||||
						})
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					logger.Error("failed to read envelope", "err", err)
 | 
					 | 
				
			||||||
					return rr, err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		rr.List = append(rr.List, mail)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rr.Tag = uint32(maxUID)
 | 
					 | 
				
			||||||
	return rr, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func FormatFlags(flags []imap.Flag) []string {
 | 
					 | 
				
			||||||
	result := make([]string, len(flags))
 | 
					 | 
				
			||||||
	for i, flag := range flags {
 | 
					 | 
				
			||||||
		result[i] = string(flag)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func ParseFlags(flags []string) []imap.Flag {
 | 
					 | 
				
			||||||
	result := make([]imap.Flag, len(flags))
 | 
					 | 
				
			||||||
	for i, flag := range flags {
 | 
					 | 
				
			||||||
		result[i] = imap.Flag(flag)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 地址格式化
 | 
					 | 
				
			||||||
func FormatAddress(addr *imap.Address) string {
 | 
					 | 
				
			||||||
	if addr == nil {
 | 
					 | 
				
			||||||
		return ""
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if addr.Name != "" {
 | 
					 | 
				
			||||||
		return fmt.Sprintf("%s <%s>", addr.Name, addr.Addr())
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return addr.Addr()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// 地址列表格式化
 | 
					 | 
				
			||||||
func FormatAddressList(addrs []imap.Address) []string {
 | 
					 | 
				
			||||||
	result := make([]string, len(addrs))
 | 
					 | 
				
			||||||
	for i, addr := range addrs {
 | 
					 | 
				
			||||||
		result[i] = FormatAddress(&addr)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										65
									
								
								mail.go
									
									
									
									
									
								
							
							
						
						
									
										65
									
								
								mail.go
									
									
									
									
									
								
							@ -1,65 +0,0 @@
 | 
				
			|||||||
package mail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"net/mail"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"apigo.cc/gojs/goja"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Mailbox struct {
 | 
					 | 
				
			||||||
	smtpHost   string
 | 
					 | 
				
			||||||
	smtpPort   int
 | 
					 | 
				
			||||||
	imapHost   string
 | 
					 | 
				
			||||||
	imapPort   int
 | 
					 | 
				
			||||||
	username   string
 | 
					 | 
				
			||||||
	password   string
 | 
					 | 
				
			||||||
	senderName string
 | 
					 | 
				
			||||||
	senderMail string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Attachment struct {
 | 
					 | 
				
			||||||
	Path        string
 | 
					 | 
				
			||||||
	Name        string
 | 
					 | 
				
			||||||
	Data        string
 | 
					 | 
				
			||||||
	ContentType string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *Mailbox) Recv(vm *goja.Runtime, opt *RecvOption) (*RecvResult, error) {
 | 
					 | 
				
			||||||
	c, err := obj.OpenIMAP(vm)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer c.Close(vm)
 | 
					 | 
				
			||||||
	return c.Recv(vm, opt)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *Mailbox) ParseAddr(addr string) (name, address string, err error) {
 | 
					 | 
				
			||||||
	r, err := mail.ParseAddress(addr)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return "", "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return r.Name, r.Address, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *Mailbox) ParseAddrList(addrs []string) ([]map[string]string, error) {
 | 
					 | 
				
			||||||
	result := make([]map[string]string, len(addrs))
 | 
					 | 
				
			||||||
	for i, addr := range addrs {
 | 
					 | 
				
			||||||
		name, address, err := obj.ParseAddr(addr)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		result[i] = map[string]string{
 | 
					 | 
				
			||||||
			"name":    name,
 | 
					 | 
				
			||||||
			"address": address,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return result, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *Mailbox) FormatAddr(name, address string) string {
 | 
					 | 
				
			||||||
	addr := &mail.Address{
 | 
					 | 
				
			||||||
		Name:    name,
 | 
					 | 
				
			||||||
		Address: address,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return addr.String()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										31
									
								
								mail_test.js
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								mail_test.js
									
									
									
									
									
								
							@ -1,31 +0,0 @@
 | 
				
			|||||||
import mail from 'apigo.cc/gojs/mail'
 | 
					 | 
				
			||||||
import co from 'apigo.cc/gojs/console'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// mail.send(['star3s@126.com'], '测试邮件', '这是一个测试邮件<br/><img src="cid:1.png"/> !!', {
 | 
					 | 
				
			||||||
//     cc: ['isstar3s@126.com'],
 | 
					 | 
				
			||||||
//     html: true,
 | 
					 | 
				
			||||||
//     embeds: [{ name: '1.png', data: 'iVBORw0KGgoAAAANSUhEUgAAABEAAAATCAMAAABBexbDAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAFZaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Chle4QcAAABjUExURaOju6yiu6ajtqKlt7ijttenuaCmvcipuOezvKStwbW2yqSgudSsuNyyuaSpvZeasua3uqWjtcSwv9XR6MS80ei0ys3H3audsLKswruqu8qjvOKrxOKtudOwveTe8+i3xMXC2TWaOysAAADFSURBVBjTRc7tssMQEAZgqW8VFBVEmtz/VZ5dOZ2+M368j92BEMzjdecBmSAI/dEEIegdKM8bxLeT5xStqXOUign/4pyDQxYMitMG4xYgtoFooU0q2VQYYIyhmHx9PgmIMSk7bl2poMTWmOQcpJqSUrpyjBJg4AdqLqVk6NbyoUCWWluMh4X7NQSFD7YJYwylQljJwnrH+VVhDisJY51z2A+wsB4Sfth757s6T5i30DdPON/39/s85/3mvSfYIYdsL+ze/wG5Qw7KZj0DQwAAAABJRU5ErkJggg==' }],
 | 
					 | 
				
			||||||
// })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// let imap = mail.openIMAP()
 | 
					 | 
				
			||||||
// let boxes = imap.listMailbox()
 | 
					 | 
				
			||||||
// co.info(boxes)
 | 
					 | 
				
			||||||
// imap.close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let r = mail.recv({
 | 
					 | 
				
			||||||
    limit: 1,
 | 
					 | 
				
			||||||
    // subject: ['审核'],
 | 
					 | 
				
			||||||
    from: ['测试工程师'],
 | 
					 | 
				
			||||||
    // search: ['腾讯云备案管局审核通过通知'],
 | 
					 | 
				
			||||||
    // not: ['审核'],
 | 
					 | 
				
			||||||
})
 | 
					 | 
				
			||||||
co.info(r)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// r = mail.recv({
 | 
					 | 
				
			||||||
//     limit: 5,
 | 
					 | 
				
			||||||
//     tag: r.tag,
 | 
					 | 
				
			||||||
// })
 | 
					 | 
				
			||||||
// co.info(r)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
return true
 | 
					 | 
				
			||||||
							
								
								
									
										224
									
								
								plugin.go
									
									
									
									
									
								
							
							
						
						
									
										224
									
								
								plugin.go
									
									
									
									
									
								
							@ -1,68 +1,41 @@
 | 
				
			|||||||
package mail
 | 
					package plugin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"runtime"
 | 
						"crypto/tls"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"apigo.cc/gojs"
 | 
						"apigo.cc/gojs"
 | 
				
			||||||
	"apigo.cc/gojs/goja"
 | 
						"apigo.cc/gojs/goja"
 | 
				
			||||||
 | 
						"github.com/jaytaylor/html2text"
 | 
				
			||||||
	"github.com/ssgo/config"
 | 
						"github.com/ssgo/config"
 | 
				
			||||||
	"github.com/ssgo/log"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
 | 
						"gopkg.in/gomail.v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const pluginName = "mail"
 | 
					const pluginName = "mail"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Config struct {
 | 
					var defaultObjectName = "default"
 | 
				
			||||||
	Mailbox          map[string]*MailboxConfig
 | 
					 | 
				
			||||||
	ClientName       string
 | 
					 | 
				
			||||||
	ClientVersion    string
 | 
					 | 
				
			||||||
	ClientOS         string
 | 
					 | 
				
			||||||
	ClientVendor     string
 | 
					 | 
				
			||||||
	ClientSupportURL string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type MailboxConfig struct {
 | 
					 | 
				
			||||||
	SmtpHost   string
 | 
					 | 
				
			||||||
	SmtpPort   int
 | 
					 | 
				
			||||||
	ImapHost   string
 | 
					 | 
				
			||||||
	ImapPort   int
 | 
					 | 
				
			||||||
	Username   string
 | 
					 | 
				
			||||||
	Password   string
 | 
					 | 
				
			||||||
	SenderName string
 | 
					 | 
				
			||||||
	SenderMail string
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var conf = &Config{
 | 
					 | 
				
			||||||
	Mailbox:          make(map[string]*MailboxConfig),
 | 
					 | 
				
			||||||
	ClientName:       "Apigo Mail Client",
 | 
					 | 
				
			||||||
	ClientVersion:    "1.0.0",
 | 
					 | 
				
			||||||
	ClientOS:         runtime.GOOS,
 | 
					 | 
				
			||||||
	ClientVendor:     "apigo.cc",
 | 
					 | 
				
			||||||
	ClientSupportURL: "https://apigo.cc/gojs/mail",
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var defaultObject = &DefaultObject{}
 | 
					var defaultObject = &DefaultObject{}
 | 
				
			||||||
var confAes *u.Aes = u.NewAes([]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"), []byte("VFs7@sK61cj^f?HZ"))
 | 
					var confAes *u.Aes = u.NewAes([]byte("?GQ$0K0GgLdO=f+~L68PLm$uhKr4'=tV"), []byte("VFs7@sK61cj^f?HZ"))
 | 
				
			||||||
var objects = make(map[string]*Mailbox)
 | 
					var objects = make(map[string]*Object)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func init() {
 | 
					func init() {
 | 
				
			||||||
	tsCode := gojs.MakeTSCode(defaultObject)
 | 
						tsCode := gojs.MakeTSCode(defaultObject)
 | 
				
			||||||
	gojs.Register("apigo.cc/gojs/"+pluginName, gojs.Module{
 | 
						gojs.Register("apigo.cc/gojs/"+pluginName, gojs.Module{
 | 
				
			||||||
		ObjectMaker: func(vm *goja.Runtime) gojs.Map {
 | 
							ObjectMaker: func(vm *goja.Runtime) gojs.Map {
 | 
				
			||||||
			config.LoadConfig(pluginName, conf)
 | 
								configs := make(map[string]*Config)
 | 
				
			||||||
			for name, object := range conf.Mailbox {
 | 
								config.LoadConfig(pluginName, &configs)
 | 
				
			||||||
				objects[name] = defaultObject.New(object)
 | 
								for name, object := range configs {
 | 
				
			||||||
			}
 | 
									objects[name] = defaultObject.New(*object)
 | 
				
			||||||
			if defaultMailbox, err := defaultObject.Get("default"); err == nil {
 | 
					 | 
				
			||||||
				defaultObject.Mailbox = defaultMailbox
 | 
					 | 
				
			||||||
			} else if len(objects) == 0 {
 | 
					 | 
				
			||||||
				log.DefaultLogger.Warning("no mailbox configured", "example for env.yml", "mail:\n  mailbox:\n    default:\n      username: ****@***.***")
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
								defaultObject.Object = defaultObject.GetInstance("default")
 | 
				
			||||||
			return gojs.ToMap(defaultObject)
 | 
								return gojs.ToMap(defaultObject)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		TsCode: tsCode,
 | 
							TsCode: tsCode,
 | 
				
			||||||
		Desc:   "mail api",
 | 
							Desc:   "mail api by github.com/ssgo/mail",
 | 
				
			||||||
		SetSSKey: func(key, iv []byte) {
 | 
							SetSSKey: func(key, iv []byte) {
 | 
				
			||||||
			confAes = u.NewAes(key, iv)
 | 
								confAes = u.NewAes(key, iv)
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
@ -70,18 +43,18 @@ func init() {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DefaultObject struct {
 | 
					type DefaultObject struct {
 | 
				
			||||||
	*Mailbox
 | 
						*Object
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (obj *DefaultObject) Get(name string) (*Mailbox, error) {
 | 
					func (obj *DefaultObject) GetInstance(name string) *Object {
 | 
				
			||||||
	if o, ok := objects[name]; ok {
 | 
						if o, ok := objects[name]; ok {
 | 
				
			||||||
		return o, nil
 | 
							return o
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		return nil, gojs.Err("mailbox [" + name + "] not configured")
 | 
							return obj.Object
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (obj *DefaultObject) New(conf *MailboxConfig) *Mailbox {
 | 
					func (obj *DefaultObject) New(conf Config) *Object {
 | 
				
			||||||
	a := strings.SplitN(conf.Username, "@", 2)
 | 
						a := strings.SplitN(conf.Username, "@", 2)
 | 
				
			||||||
	if len(a) == 2 {
 | 
						if len(a) == 2 {
 | 
				
			||||||
		if conf.SmtpHost == "" {
 | 
							if conf.SmtpHost == "" {
 | 
				
			||||||
@ -97,11 +70,8 @@ func (obj *DefaultObject) New(conf *MailboxConfig) *Mailbox {
 | 
				
			|||||||
			conf.ImapPort = 993
 | 
								conf.ImapPort = 993
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if conf.SenderMail == "" {
 | 
						fmt.Println(u.BMagenta(u.JsonP(conf)), 111)
 | 
				
			||||||
		conf.SenderMail = conf.Username
 | 
						return &Object{
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return &Mailbox{
 | 
					 | 
				
			||||||
		smtpHost:   conf.SmtpHost,
 | 
							smtpHost:   conf.SmtpHost,
 | 
				
			||||||
		smtpPort:   conf.SmtpPort,
 | 
							smtpPort:   conf.SmtpPort,
 | 
				
			||||||
		imapHost:   conf.ImapHost,
 | 
							imapHost:   conf.ImapHost,
 | 
				
			||||||
@ -109,6 +79,154 @@ func (obj *DefaultObject) New(conf *MailboxConfig) *Mailbox {
 | 
				
			|||||||
		username:   conf.Username,
 | 
							username:   conf.Username,
 | 
				
			||||||
		password:   confAes.DecryptUrlBase64ToString(conf.Password),
 | 
							password:   confAes.DecryptUrlBase64ToString(conf.Password),
 | 
				
			||||||
		senderName: conf.SenderName,
 | 
							senderName: conf.SenderName,
 | 
				
			||||||
		senderMail: conf.SenderMail,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Config struct {
 | 
				
			||||||
 | 
						SmtpHost   string
 | 
				
			||||||
 | 
						SmtpPort   int
 | 
				
			||||||
 | 
						ImapHost   string
 | 
				
			||||||
 | 
						ImapPort   int
 | 
				
			||||||
 | 
						Username   string
 | 
				
			||||||
 | 
						Password   string
 | 
				
			||||||
 | 
						SenderName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Object struct {
 | 
				
			||||||
 | 
						smtpHost   string
 | 
				
			||||||
 | 
						smtpPort   int
 | 
				
			||||||
 | 
						imapHost   string
 | 
				
			||||||
 | 
						imapPort   int
 | 
				
			||||||
 | 
						username   string
 | 
				
			||||||
 | 
						password   string
 | 
				
			||||||
 | 
						senderName string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Attachment struct {
 | 
				
			||||||
 | 
						Path        string
 | 
				
			||||||
 | 
						Name        string
 | 
				
			||||||
 | 
						Data        string
 | 
				
			||||||
 | 
						ContentType string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SendOption struct {
 | 
				
			||||||
 | 
						Cc          []string
 | 
				
			||||||
 | 
						Bcc         []string
 | 
				
			||||||
 | 
						Html        bool
 | 
				
			||||||
 | 
						Attachments []Attachment
 | 
				
			||||||
 | 
						Embeds      []Attachment
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (obj *Object) Send(vm *goja.Runtime, to []string, subject, content string, option *SendOption) error {
 | 
				
			||||||
 | 
						logger := gojs.GetLogger(vm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg := gomail.NewMessage()
 | 
				
			||||||
 | 
						if obj.senderName != "" {
 | 
				
			||||||
 | 
							msg.SetHeader("From", msg.FormatAddress(obj.username, obj.senderName))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							msg.SetHeader("From", obj.username)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg.SetHeader("To", to...)
 | 
				
			||||||
 | 
						msg.SetHeader("Subject", subject)
 | 
				
			||||||
 | 
						useHtml := false
 | 
				
			||||||
 | 
						if option != nil {
 | 
				
			||||||
 | 
							if len(option.Cc) > 0 {
 | 
				
			||||||
 | 
								msg.SetHeader("Cc", option.Cc...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(option.Bcc) > 0 {
 | 
				
			||||||
 | 
								msg.SetHeader("Bcc", option.Bcc...)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(option.Attachments) > 0 {
 | 
				
			||||||
 | 
								for _, attachment := range option.Attachments {
 | 
				
			||||||
 | 
									if attachment.Path != "" {
 | 
				
			||||||
 | 
										if u.FileExists(attachment.Path) {
 | 
				
			||||||
 | 
											msg.Attach(attachment.Path)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											logger.Error("mail attachment file not exists", "filename", attachment.Path, "to", to, "subject", subject)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else if attachment.Data != "" {
 | 
				
			||||||
 | 
										if attachment.Name == "" {
 | 
				
			||||||
 | 
											attachment.Name = "attachment"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if attachment.ContentType == "" {
 | 
				
			||||||
 | 
											attachment.ContentType = "text/plain"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										buf, err := base64.StdEncoding.DecodeString(attachment.Data)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											buf = []byte(attachment.Data)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										msg.Attach(attachment.Name, gomail.SetCopyFunc(func(w io.Writer) error {
 | 
				
			||||||
 | 
											_, err := w.Write(buf)
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}), gomail.SetHeader(map[string][]string{
 | 
				
			||||||
 | 
											"Content-Type": {attachment.ContentType},
 | 
				
			||||||
 | 
										}))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(option.Embeds) > 0 {
 | 
				
			||||||
 | 
								for _, attachment := range option.Embeds {
 | 
				
			||||||
 | 
									if attachment.Path != "" {
 | 
				
			||||||
 | 
										if u.FileExists(attachment.Path) {
 | 
				
			||||||
 | 
											msg.Attach(attachment.Path)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											logger.Error("mail embed file not exists", "filename", attachment.Path, "to", to, "subject", subject)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									} else if attachment.Data != "" {
 | 
				
			||||||
 | 
										if attachment.Name == "" {
 | 
				
			||||||
 | 
											attachment.Name = "image"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if attachment.ContentType == "" {
 | 
				
			||||||
 | 
											attachment.ContentType = "image/png"
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										buf, err := base64.StdEncoding.DecodeString(attachment.Data)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											buf = []byte(attachment.Data)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										msg.Embed(attachment.Name, gomail.SetCopyFunc(func(w io.Writer) error {
 | 
				
			||||||
 | 
											_, err := w.Write(buf)
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}), gomail.SetHeader(map[string][]string{
 | 
				
			||||||
 | 
											"Content-Type": {attachment.ContentType},
 | 
				
			||||||
 | 
											"Content-ID":   {"<" + attachment.Name + ">"},
 | 
				
			||||||
 | 
										}))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							useHtml = option.Html
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if useHtml {
 | 
				
			||||||
 | 
							msg.SetBody("text/html", content)
 | 
				
			||||||
 | 
							textContent, err := html2text.FromString(content, html2text.Options{TextOnly: true, PrettyTables: true})
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								logger.Error("convert html to text failed", "err", err, "to", to, "subject", subject, "content", content)
 | 
				
			||||||
 | 
								textContent = content
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							msg.AddAlternative("text/plain", textContent)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							msg.SetBody("text/plain", content)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						conn := gomail.NewDialer(obj.smtpHost, obj.smtpPort, obj.username, obj.password)
 | 
				
			||||||
 | 
						switch obj.smtpPort {
 | 
				
			||||||
 | 
						case 465:
 | 
				
			||||||
 | 
							conn.SSL = true
 | 
				
			||||||
 | 
						case 587:
 | 
				
			||||||
 | 
							conn.TLSConfig = &tls.Config{
 | 
				
			||||||
 | 
								ServerName:         obj.smtpHost,
 | 
				
			||||||
 | 
								InsecureSkipVerify: false,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// conn.Auth = smtp.PlainAuth("", obj.username, obj.password, obj.smtpHost)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err := conn.DialAndSend(msg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							logger.Error("send mail failed", "err", err.Error(), "to", to, "subject", "from", obj.username, subject, "content", content)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							logger.Info("send mail success", "to", to, "from", obj.username, "subject", subject)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,65 +1,16 @@
 | 
				
			|||||||
package mail_test
 | 
					package plugin_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"apigo.cc/gojs"
 | 
						"apigo.cc/gojs"
 | 
				
			||||||
	_ "apigo.cc/gojs/console"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
						"github.com/ssgo/u"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestPlugin(t *testing.T) {
 | 
					func TestPlugin(t *testing.T) {
 | 
				
			||||||
	// c, err := imapclient.DialTLS("imap.126.com:993", nil)
 | 
					 | 
				
			||||||
	// if err != nil {
 | 
					 | 
				
			||||||
	// 	log.Fatalf("failed to dial IMAP server: %v", err)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
	// defer c.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// c.ID(&imap.IDData{
 | 
					 | 
				
			||||||
	// 	Name:       "Apigo Mail Client",
 | 
					 | 
				
			||||||
	// 	Version:    "1.0.0",
 | 
					 | 
				
			||||||
	// 	Vendor:     "apigo.cc",
 | 
					 | 
				
			||||||
	// 	SupportURL: "https://apigo.cc/gojs/mail",
 | 
					 | 
				
			||||||
	// 	OS:         runtime.GOOS,
 | 
					 | 
				
			||||||
	// })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// if err := c.Login("api_go@126.com", "ZZEQWMBONBXZGVKY").Wait(); err != nil {
 | 
					 | 
				
			||||||
	// 	log.Fatalf("failed to login: %v", err)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// mailboxes, err := c.List("", "%", nil).Collect()
 | 
					 | 
				
			||||||
	// if err != nil {
 | 
					 | 
				
			||||||
	// 	log.Fatalf("failed to list mailboxes: %v", err)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
	// log.Printf("Found %v mailboxes", len(mailboxes))
 | 
					 | 
				
			||||||
	// for _, mbox := range mailboxes {
 | 
					 | 
				
			||||||
	// 	log.Printf(" - %v", mbox.Mailbox)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// selectedMbox, err := c.Select("INBOX", nil).Wait()
 | 
					 | 
				
			||||||
	// if err != nil {
 | 
					 | 
				
			||||||
	// 	log.Fatalf("failed to select INBOX: %v", err)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
	// log.Printf("INBOX contains %v messages", selectedMbox.NumMessages)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// if selectedMbox.NumMessages > 0 {
 | 
					 | 
				
			||||||
	// 	seqSet := imap.SeqSetNum(1)
 | 
					 | 
				
			||||||
	// 	fetchOptions := &imap.FetchOptions{Envelope: true}
 | 
					 | 
				
			||||||
	// 	messages, err := c.Fetch(seqSet, fetchOptions).Collect()
 | 
					 | 
				
			||||||
	// 	if err != nil {
 | 
					 | 
				
			||||||
	// 		log.Fatalf("failed to fetch first message in INBOX: %v", err)
 | 
					 | 
				
			||||||
	// 	}
 | 
					 | 
				
			||||||
	// 	log.Printf("subject of first message in INBOX: %v", messages[0].Envelope.Subject)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// if err := c.Logout().Wait(); err != nil {
 | 
					 | 
				
			||||||
	// 	log.Fatalf("failed to logout: %v", err)
 | 
					 | 
				
			||||||
	// }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// return
 | 
					 | 
				
			||||||
	gojs.ExportForDev()
 | 
						gojs.ExportForDev()
 | 
				
			||||||
	r, err := gojs.RunFile("mail_test.js")
 | 
						r, err := gojs.RunFile("plugin_test.js")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	} else if r != true {
 | 
						} else if r != true {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										9
									
								
								plugin_test.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								plugin_test.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					import mail from 'apigo.cc/gojs/mail'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					mail.send(['star3s@126.com'], '测试邮件', '这是一个测试邮件<br/><img src="cid:1.png"/> !!', {
 | 
				
			||||||
 | 
					    cc: ['isstar3s@126.com'],
 | 
				
			||||||
 | 
					    html: true,
 | 
				
			||||||
 | 
					    embeds: [{ name: '1.png', data: 'iVBORw0KGgoAAAANSUhEUgAAABEAAAATCAMAAABBexbDAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAFZaVRYdFhNTDpjb20uYWRvYmUueG1wAAAAAAA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA2LjAuMCI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIj4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Chle4QcAAABjUExURaOju6yiu6ajtqKlt7ijttenuaCmvcipuOezvKStwbW2yqSgudSsuNyyuaSpvZeasua3uqWjtcSwv9XR6MS80ei0ys3H3audsLKswruqu8qjvOKrxOKtudOwveTe8+i3xMXC2TWaOysAAADFSURBVBjTRc7tssMQEAZgqW8VFBVEmtz/VZ5dOZ2+M368j92BEMzjdecBmSAI/dEEIegdKM8bxLeT5xStqXOUign/4pyDQxYMitMG4xYgtoFooU0q2VQYYIyhmHx9PgmIMSk7bl2poMTWmOQcpJqSUrpyjBJg4AdqLqVk6NbyoUCWWluMh4X7NQSFD7YJYwylQljJwnrH+VVhDisJY51z2A+wsB4Sfth757s6T5i30DdPON/39/s85/3mvSfYIYdsL+ze/wG5Qw7KZj0DQwAAAABJRU5ErkJggg==' }],
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					return true
 | 
				
			||||||
							
								
								
									
										135
									
								
								smtp.go
									
									
									
									
									
								
							
							
						
						
									
										135
									
								
								smtp.go
									
									
									
									
									
								
							@ -1,135 +0,0 @@
 | 
				
			|||||||
package mail
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"crypto/tls"
 | 
					 | 
				
			||||||
	"encoding/base64"
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"apigo.cc/gojs"
 | 
					 | 
				
			||||||
	"apigo.cc/gojs/goja"
 | 
					 | 
				
			||||||
	"github.com/jaytaylor/html2text"
 | 
					 | 
				
			||||||
	"github.com/ssgo/u"
 | 
					 | 
				
			||||||
	"gopkg.in/gomail.v2"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type SendOption struct {
 | 
					 | 
				
			||||||
	Cc          []string
 | 
					 | 
				
			||||||
	Bcc         []string
 | 
					 | 
				
			||||||
	Html        bool
 | 
					 | 
				
			||||||
	Attachments []Attachment
 | 
					 | 
				
			||||||
	Embeds      []Attachment
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (obj *Mailbox) Send(vm *goja.Runtime, to []string, subject, content string, option *SendOption) error {
 | 
					 | 
				
			||||||
	logger := gojs.GetLogger(vm)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg := gomail.NewMessage()
 | 
					 | 
				
			||||||
	if obj.senderName != "" {
 | 
					 | 
				
			||||||
		msg.SetHeader("From", msg.FormatAddress(obj.username, obj.senderName))
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		msg.SetHeader("From", obj.username)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg.SetHeader("To", to...)
 | 
					 | 
				
			||||||
	msg.SetHeader("Subject", subject)
 | 
					 | 
				
			||||||
	useHtml := false
 | 
					 | 
				
			||||||
	if option != nil {
 | 
					 | 
				
			||||||
		if len(option.Cc) > 0 {
 | 
					 | 
				
			||||||
			msg.SetHeader("Cc", option.Cc...)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(option.Bcc) > 0 {
 | 
					 | 
				
			||||||
			msg.SetHeader("Bcc", option.Bcc...)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(option.Attachments) > 0 {
 | 
					 | 
				
			||||||
			for _, attachment := range option.Attachments {
 | 
					 | 
				
			||||||
				if attachment.Path != "" {
 | 
					 | 
				
			||||||
					if u.FileExists(attachment.Path) {
 | 
					 | 
				
			||||||
						msg.Attach(attachment.Path)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						logger.Error("mail attachment file not exists", "filename", attachment.Path, "to", to, "subject", subject)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				} else if attachment.Data != "" {
 | 
					 | 
				
			||||||
					if attachment.Name == "" {
 | 
					 | 
				
			||||||
						attachment.Name = "attachment"
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if attachment.ContentType == "" {
 | 
					 | 
				
			||||||
						attachment.ContentType = "text/plain"
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					buf, err := base64.StdEncoding.DecodeString(attachment.Data)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						buf = []byte(attachment.Data)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					msg.Attach(attachment.Name, gomail.SetCopyFunc(func(w io.Writer) error {
 | 
					 | 
				
			||||||
						_, err := w.Write(buf)
 | 
					 | 
				
			||||||
						return err
 | 
					 | 
				
			||||||
					}), gomail.SetHeader(map[string][]string{
 | 
					 | 
				
			||||||
						"Content-Type": {attachment.ContentType},
 | 
					 | 
				
			||||||
					}))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if len(option.Embeds) > 0 {
 | 
					 | 
				
			||||||
			for _, attachment := range option.Embeds {
 | 
					 | 
				
			||||||
				if attachment.Path != "" {
 | 
					 | 
				
			||||||
					if u.FileExists(attachment.Path) {
 | 
					 | 
				
			||||||
						msg.Attach(attachment.Path)
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						logger.Error("mail embed file not exists", "filename", attachment.Path, "to", to, "subject", subject)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				} else if attachment.Data != "" {
 | 
					 | 
				
			||||||
					if attachment.Name == "" {
 | 
					 | 
				
			||||||
						attachment.Name = "image"
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					if attachment.ContentType == "" {
 | 
					 | 
				
			||||||
						attachment.ContentType = "image/png"
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					buf, err := base64.StdEncoding.DecodeString(attachment.Data)
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						buf = []byte(attachment.Data)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					msg.Embed(attachment.Name, gomail.SetCopyFunc(func(w io.Writer) error {
 | 
					 | 
				
			||||||
						_, err := w.Write(buf)
 | 
					 | 
				
			||||||
						return err
 | 
					 | 
				
			||||||
					}), gomail.SetHeader(map[string][]string{
 | 
					 | 
				
			||||||
						"Content-Type": {attachment.ContentType},
 | 
					 | 
				
			||||||
						"Content-ID":   {"<" + attachment.Name + ">"},
 | 
					 | 
				
			||||||
					}))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		useHtml = option.Html
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if useHtml {
 | 
					 | 
				
			||||||
		msg.SetBody("text/html", content)
 | 
					 | 
				
			||||||
		textContent, err := html2text.FromString(content, html2text.Options{TextOnly: true, PrettyTables: true})
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			logger.Error("convert html to text failed", "err", err, "to", to, "subject", subject, "content", content)
 | 
					 | 
				
			||||||
			textContent = content
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		msg.AddAlternative("text/plain", textContent)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		msg.SetBody("text/plain", content)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	conn := gomail.NewDialer(obj.smtpHost, obj.smtpPort, obj.username, obj.password)
 | 
					 | 
				
			||||||
	switch obj.smtpPort {
 | 
					 | 
				
			||||||
	case 465:
 | 
					 | 
				
			||||||
		conn.SSL = true
 | 
					 | 
				
			||||||
	case 587:
 | 
					 | 
				
			||||||
		conn.TLSConfig = &tls.Config{
 | 
					 | 
				
			||||||
			ServerName:         obj.smtpHost,
 | 
					 | 
				
			||||||
			InsecureSkipVerify: false,
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// conn.Auth = smtp.PlainAuth("", obj.username, obj.password, obj.smtpHost)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err := conn.DialAndSend(msg)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		logger.Error("send mail failed", "err", err.Error(), "to", to, "subject", "from", obj.username, subject, "content", content)
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		logger.Info("send mail success", "to", to, "from", obj.username, "subject", subject)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user