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(.*)+)_(?P(.*)+)】(?P(.*)+)_(?P(.*)+)") // 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[a-zA-z\s]+)from(?P[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 }