123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- package resource
- import (
- "errors"
- "github.com/emersion/go-imap"
- "github.com/emersion/go-imap/client"
- "github.com/emersion/go-message/charset"
- "github.com/emersion/go-message/mail"
- "hongze/fms_api/global"
- "hongze/fms_api/utils"
- "io"
- "io/ioutil"
- "os"
- "regexp"
- "strings"
- "time"
- )
- type EmailImapService struct {
- Client *client.Client
- }
- func NewEmailImapService() *EmailImapService {
- global.LOG.Info("Connecting to server...")
- // Connect to server
- //c, err := client.DialTLS("imap.qq.com:993", nil)
- var emailHost string
- if global.CONFIG.Serve.RunMode != "debug" {
- emailHost = "imap.qiye.aliyun.com:993"
- } else {
- //测试邮箱
- emailHost = "imap.aliyun.com:993"
- }
- c, err := client.DialTLS(emailHost, nil)
- if err != nil {
- panic("连接邮箱服务器失败:" + err.Error())
- }
- global.LOG.Info("Connected success")
- return &EmailImapService{Client: c}
- }
- type AttachmentListItem struct {
- Title string
- Date time.Time
- Name string
- Position string
- FullPath string
- FileName string
- FromEmails []string
- LoadUrl string
- }
- //DownLoadCv 下载简历附件接口
- func (e *EmailImapService) DownLoadEmailCv(username, password string, attachmentChan chan *AttachmentListItem) (err error) {
- // Don't forget to logout
- defer e.Client.Logout()
- defer close(attachmentChan)
- // Login
- if err = e.Client.Login(username, password); err != nil {
- global.LOG.Info("login failed err: " + err.Error())
- return
- }
- global.LOG.Info("login in")
- // Select INBOX
- mbox, err := e.Client.Select("INBOX", false)
- if err != nil {
- global.LOG.Error(err)
- return
- }
- // Get the last message
- if mbox.Messages == 0 {
- global.LOG.Error("No message in mailbox")
- return
- }
- /*// 筛选最近7天的邮件
- //criteria := imap.NewSearchCriteria()
- //criteria.WithoutFlags = []string{imap.SeenFlag}
- //criteria.SentSince = time.Date(1984, 11, 5, 0, 0, 0, 0, time.UTC)
- //ids, err := e.Client.Search(criteria)
- if err != nil {
- global.LOG.Error("No search message in mailbox"+err.Error())
- return
- }
- if len(ids) == 0 {
- global.LOG.Error("No search message in mailbox")
- return
- }
- seqSet := new(imap.SeqSet)
- seqSet.AddNum(ids...)*/
- seqSet := new(imap.SeqSet)
- from := uint32(1)
- to := mbox.Messages
- if mbox.Messages > 100 {
- // We're using unsigned integers here, only subtract if the result is > 0
- from = mbox.Messages - 100
- }
- seqSet.AddRange(from, to)
- global.LOG.Infof("mbox.Messages length :%d", mbox.Messages)
- // Get the whole message body
- var section imap.BodySectionName
- items := []imap.FetchItem{section.FetchItem()}
- messages := make(chan *imap.Message, mbox.Messages)
- done := make(chan error, 1)
- go func() {
- done <- e.Client.Fetch(seqSet, items, messages)
- }()
- global.LOG.Info("最近7天收到的邮件:")
- imap.CharsetReader = charset.Reader
- beforeDate7 := time.Now().AddDate(0, 0, -7)
- for msg := range messages {
- if msg == nil {
- err = errors.New("Server didn't returned message")
- global.LOG.Error("Server didn't returned message")
- return
- }
- tmp := new(AttachmentListItem)
- section = imap.BodySectionName{}
- r := msg.GetBody(§ion)
- if r == nil {
- err = errors.New("Server didn't returned message body")
- global.LOG.Error("Server didn't returned message body")
- return
- }
- var mr *mail.Reader
- mr, err = mail.CreateReader(r)
- if err != nil {
- global.LOG.Error(err)
- return
- }
- // Print some info about the message
- header := mr.Header
- var emailDate time.Time
- if emailDate, err = header.Date(); err == nil {
- global.LOG.Infof("Date:%s", emailDate.Format(utils.FormatDateTime))
- if emailDate.Before(beforeDate7) {
- continue
- }
- //global.LOG.Infof("Date:", emailDate)
- tmp.Date = emailDate
- }
- if subject, err := header.Subject(); err == nil {
- global.LOG.Infof("Subject:%s", subject)
- newSubject, info, flag := DealSubject(subject)
- if !flag {
- continue
- }
- tmp.Title = newSubject
- tmp.Name = info.Name
- tmp.Position = info.Position
- }
- if from, err := header.AddressList("From"); err == nil {
- global.LOG.Infof("From:%s", from)
- //tmp.SenderEmails = from
- for _, fa := range from {
- tmp.FromEmails = append(tmp.FromEmails, fa.Address)
- }
- }
- if to, err := header.AddressList("To"); err == nil {
- global.LOG.Infof("To:%s", to)
- }
- // Process each message's part
- for {
- p, tErr := mr.NextPart()
- if tErr == io.EOF {
- break
- } else if tErr != nil {
- global.LOG.Errorf("Process each message's part err:%v", tErr)
- return
- }
- switch h := p.Header.(type) {
- case *mail.InlineHeader:
- // This is the message's text (can be plain-text or HTML)
- _, _ = ioutil.ReadAll(p.Body)
- //global.LOG.Infof("Got text: %v", string(b))
- case *mail.AttachmentHeader:
- // This is an attachment
- filename, _ := h.Filename()
- tmp.FileName = filename
- global.LOG.Infof("Got attachment: %v", filename)
- //保存到本地
- dir, tmpErr := mk_dir("download/cv/" + emailDate.Format(utils.FormatDateTimeUnSpace))
- if tmpErr != nil {
- err = tmpErr
- global.LOG.Infof("%v 写入失败 err %s", filename, err.Error())
- return
- }
- filename = dir + "/" + filename
- content, _ := ioutil.ReadAll(p.Body)
- //global.LOG.Infof("Got text: %v", string(content))
- err = write_to_file(filename, content)
- if err != nil {
- global.LOG.Infof("%v 写入失败", filename)
- return
- }
- tmp.FullPath = filename
- /*tmpFileName := strings.Split(path.Base(tmp.FileName), ".")
- tmpName := tmpFileName[0]
- ext := tmpFileName[1]
- global.LOG.Infof("Got attachment ext: %v", ext)
- if ext == "docx" {
- fileOutPath, e := FuncDocs2Pdf(global.CONFIG.Serve.LibreOfficePath, filename ,dir,"pdf")
- if e != nil {
- err = e
- global.LOG.Infof("docx 转pdf 失败:%v",e.Error())
- return
- }
- tmp.FullPath = fileOutPath
- tmp.FileName = tmpName + ".pdf"
- }*/
- //把结果放到channel中
- attachmentChan <- tmp
- }
- }
- }
- if err = <-done; err != nil {
- global.LOG.Info("email client Fetch Err: " + err.Error())
- return
- }
- return
- }
- func mk_dir(dir_path string) (string, error) {
- var path string
- if os.IsPathSeparator('\\') { //前边的判断是否是系统的分隔符
- path = "\\"
- } else {
- path = "/"
- }
- //fmt.Println(path)
- dir, _ := os.Getwd() //当前的目录
- err := os.MkdirAll(dir+path+dir_path, os.ModePerm) //在当前目录下生成md目录
- if err != nil {
- global.LOG.Infof("%v", err)
- return "", nil
- }
- return dir + path + dir_path, nil
- }
- /*
- 函数名称:write_to_file
- 函数作用:写内容到文件
- 输入参数:filename(文件名),content(内容)
- 输出参数:无
- */
- func write_to_file(filename string, content []byte) (err error) {
- var fileHandle *os.File
- /*if !checkFileIsExist(filename) { //如果文件存在
- fileHandle, err = os.Create(filename) //创建文件
- global.LOG.Infof("文件不存在")
- if err != nil {
- global.LOG.Infof("Err" + err.Error())
- }
- }else{*/
- fileHandle, err = os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_APPEND, os.ModePerm)
- //global.LOG.Infof("文件存在")
- //}
- if err != nil {
- return
- }
- defer fileHandle.Close()
- //循环读取
- // NewWriter 默认缓冲区大小是 4096
- // 需要使用自定义缓冲区的writer 使用 NewWriterSize()方法
- // buf := bufio.NewWriter(fileHandle)
- // 字节写入
- // 字符串写入
- _, err = fileHandle.WriteString(string(content))
- if err != nil {
- return
- }
- return
- }
- func DealSubject(subject string) (newSubject string, person AttachmentListItem, flag bool) {
- if strings.Contains(subject, "转发:") || strings.Contains(subject, "Fwd:") {
- reg := `^[Fwd:|转发:]+`
- re := regexp.MustCompile(reg)
- subject = re.ReplaceAllString(subject, "")
- }
- newSubject = subject
- person, flag = dealBossSubject(subject)
- if !flag {
- person, flag = dealDefaultSubject(subject)
- if !flag {
- person, flag = dealLiePinSubject(subject)
- if !flag {
- person, flag = dealOtherSubject(subject)
- }
- }
- }
- return
- }
- func dealBossSubject(subject string) (person AttachmentListItem, flag bool) {
- matched, err := regexp.MatchString(`(.*)【BOSS直聘】`, subject)
- if err != nil {
- return
- }
- if !matched {
- return
- }
- items := strings.Split(subject, "|")
- if len(items) < 3 {
- return
- }
- name := items[0]
- global.LOG.Infof("求职姓名:%s", name)
- //获取职位
- items1 := strings.Split(items[1], "应聘")
- if len(items1) < 2 {
- return
- }
- position := strings.Trim(items1[1], " ")
- global.LOG.Infof("求职岗位:%s", position)
- person.Name = name
- person.Position = position
- flag = true
- return
- }
- func dealDefaultSubject(subject string) (person AttachmentListItem, flag bool) {
- reg := `[(](.*)[)-](.*){1,10}[先生|女士]$`
- matched, err := regexp.MatchString(reg, subject)
- if err != nil {
- return
- }
- if !matched {
- return
- }
- re := regexp.MustCompile(reg)
- position := re.ReplaceAllString(subject, "")
- global.LOG.Infof("求职岗位:%s", position)
- if position == "" {
- return
- }
- //获取职位
- items := strings.Split(subject, "-")
- if len(items) < 2 {
- return
- }
- name := items[len(items)-1]
- global.LOG.Infof("求职姓名:%s", name)
- person.Name = name
- person.Position = position
- flag = true
- return
- }
- func dealLiePinSubject(subject string) (person AttachmentListItem, flag bool) {
- reg := `来自猎聘的候选人`
- matched, err := regexp.MatchString(reg, subject)
- if err != nil {
- return
- }
- if !matched {
- return
- }
- // Regex pattern captures "key: value" pair from the content.
- pattern := regexp.MustCompile("【(?P<key1>(.*)+)_(?P<key2>(.*)+)】(?P<key3>(.*)+)_(?P<key4>(.*)+)")
- // Template to convert "key: value" to "key=value" by
- // referencing the values captured by the regex pattern.
- template := "$key1=$key2=$key3=$key4\n"
- result := []byte{}
- // For each match of the regex in the content.
- for _, submatches := range pattern.FindAllStringSubmatchIndex(subject, -1) {
- // Apply the captured submatches to the template and append the output
- // to the result.
- result = pattern.ExpandString(result, template, subject, submatches)
- }
- global.LOG.Infof("求职邮件匹配结果:%s", string(result))
- //获取职位
- tmp := strings.Split(string(result), "=")
- if len(tmp) < 4 {
- return
- }
- position := strings.Trim(tmp[0], " ")
- global.LOG.Infof("求职岗位:%s", position)
- name := strings.Trim(tmp[2], " ")
- global.LOG.Infof("求职姓名:%s", name)
- person.Name = name
- person.Position = position
- flag = true
- return
- }
- func dealOtherSubject(subject string) (person AttachmentListItem, flag bool) {
- reg := `^New application`
- matched, err := regexp.MatchString(reg, subject)
- if err != nil {
- return
- }
- if !matched {
- return
- }
- // Regex pattern captures "key: value" pair from the content.
- pattern := regexp.MustCompile(`(?P<key1>[a-zA-z\s]+)from(?P<key2>[a-zA-z\s]+)`)
- // Template to convert "key: value" to "key=value" by
- // referencing the values captured by the regex pattern.
- template := "$key1=$key2\n"
- result := []byte{}
- // For each match of the regex in the content.
- for _, submatches := range pattern.FindAllStringSubmatchIndex(subject, -1) {
- // Apply the captured submatches to the template and append the output
- // to the result.
- result = pattern.ExpandString(result, template, subject, submatches)
- }
- global.LOG.Infof("求职邮件匹配结果:%s", string(result))
- //获取职位
- tmp := strings.Split(string(result), "=")
- if len(tmp) < 2 {
- return
- }
- position := strings.Trim(tmp[0], " ")
- global.LOG.Infof("求职岗位:%s", position)
- name := strings.Trim(tmp[1], " ")
- global.LOG.Infof("求职姓名:%s", name)
- person.Name = name
- person.Position = position
- flag = true
- return
- }
|