一个冷门的需求,golang实现邮件回复生成邮件链

背景:

最近有一个 场景是基于某封邮件进行回复,这样可以在收件箱内形成一个完整的邮件链,比如进行倒计时的提醒,自动邮件回复等情况还挺实用的,日常对邮件的处理主要还是发邮件进行告警等,使用imap进行回复的场景还是挺少见的,所以简单记录一下。

首次发送邮件:

  • 首次发送邮件告知用户 ,这个很简单,也是最常用的一个用法。
package main

import (
	"fmt"
	"gopkg.in/mail.v2"
	"time"
)

func main() {
	currentTime := time.Now()
	format := "2006-01-02"
	nowTimeFormat := currentTime.Format(format)
	deadLineFormat := currentTime.AddDate(0, 1, 0).Format(format)
	timeDifference := currentTime.AddDate(0, 1, 0).Sub(currentTime)
	daysDifference := int(timeDifference.Hours() / 24)

	title := fmt.Sprintf("[重要] 资产确认倒计时: %v", nowTimeFormat)
	sender := mail.NewMessage()
	sender.SetHeader("From", "xxx@mail.com")
	sender.SetHeader("To", "yyy@mail.com","zzz@mail.com")
	sender.SetHeader("Subject", title)
	htmlBody := fmt.Sprintf(`
	<head>
  <style>
    table {
      border-collapse: collapse;
      border-spacing: 0;
    }

    th, td {
      border: 1px solid black;
      padding: 8px;
      text-align: left;
    }
  </style>
</head>
	<p>各位领导:</p>

	<p>请确认以下资源的是否还在使用。</p>
	<p><b>截止时间: %v</b></p>
	<p><b>剩余时间: %v</b></p>
	<p><b>资源确认链接: https://xxxx/resource/list</b></p>
	<p><b>业务产品线: 售后</b></p>
	<table>
  <thead>
    <tr>
      <th>资源类型</th>
      <th>已确认</th>
      <th>待确认</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ECS</td>
      <td>OSS</td>
      <td>RDS</td>
    </tr>
    <tr>
      <td>100</td>
      <td>120</td>
      <td>80</td>
    </tr>
    <tr>
      <td>30</td>
      <td>20</td>
      <td>10</td>
    </tr>
  </tbody>
</table>
	<p>烦请尽快确认资源归属</p>
	<p>感谢您的支持</p>
	`, deadLineFormat, daysDifference)

	sender.SetBody("text/html", htmlBody)

	// 邮件服务器的设置
	d := mail.NewDialer("smtp.xxx.cn", 587, "xxx@mail.com", "passwd")

	// 发送邮件
	if err := d.DialAndSend(sender); err != nil {
		panic(err)
	}
}

回复指定邮件:

  • 找到自己历史上发的邮件,并进行回复
package main

import (
	"fmt"
	"github.com/emersion/go-imap"
	"github.com/emersion/go-imap/client"
	"github.com/emersion/go-message/mail"
	mailv2 "gopkg.in/mail.v2"
	"regexp"
	"time"
)
func searchMessagesFrom(c *client.Client, mailbox, from string) ([]uint32, error) {
	// 构造 SEARCH 命令
	cmd := &imap.SearchCriteria{}

	// 执行 SEARCH 命令
	ids, err := c.Search(cmd)
	if err != nil {
		return nil, err
	}
	fmt.Println(ids)
	return ids, nil
}

func decodeMIMEHeader(encoded string) (string, error) {
	// 使用 mime.WordDecoder 解码
	decoder := new(mime.WordDecoder)
	decoded, err := decoder.DecodeHeader(encoded)
	if err != nil {
		return "", err
	}
	return decoded, nil
}

func main() {
	// 连接imap服务器
	c, err := client.DialTLS("imap.xxx.cn:993", nil)
	if err != nil {
		panic(err)
	}
	defer c.Logout()

	// 登陆认证
	if err := c.Login("xxx@mail.com", "passwd"); err != nil {
		panic(err)
	}

	// 列出所有收件箱
	mailboxes := make(chan *imap.MailboxInfo, 10)
	done := make(chan error, 1)
	go func() {
		done <- c.List("", "*", mailboxes)
	}()
	for m := range mailboxes {
		fmt.Println("* " + m.Name)
	}
	if err := <-done; err != nil {
		fmt.Println(err)
	}

	// 选择发件箱
	mbox, err := c.Select("已发送", false)
	if err != nil {
		fmt.Println(err)
	}

	// 获得最新的一封邮件
	from := uint32(1)
	to := mbox.Messages
	if mbox.Messages > 0 {
		from = mbox.Messages - 0
	}
	seqset := new(imap.SeqSet)
	seqset.AddRange(from, to)

	messages := make(chan *imap.Message, 1)
	go func() {
		if err := c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope, imap.FetchRFC822, imap.FetchFlags, imap.FetchUid}, messages); err != nil {
			fmt.Println(err)
		}
	}()

	for msg := range messages {
		// 解析邮件头
		r := msg.GetBody(&imap.BodySectionName{})
		if r == nil {
			fmt.Println("Email body is empty.")
			continue
		}

		mr, err := mail.CreateReader(r)
		if err != nil {
			fmt.Println(err)
		}
		header := mr.Header

		// 打印邮件主题和发件人
		// 解码 Subject
		decodedSubject, err := decodeMIMEHeader(msg.Envelope.Subject)
		if err != nil {
			fmt.Println(err)
		}

		keyWord := "aass"
		re := regexp.MustCompile(`\[.*` + keyWord + `.*\]`)
		match := re.FindStringSubmatch(decodedSubject)
		if len(match) != 0 {
			fmt.Println("匹配到keyword邮件")
			// 使用net/smtp库发送邮件
			currentTime := time.Now()
			format := "2006-01-02"
			nowTimeFormat := currentTime.Format(format)
			deadLineFormat := currentTime.AddDate(0, 1, 0).Format(format)
			timeDifference := currentTime.AddDate(0, 1, 0).Sub(currentTime)
			daysDifference := int(timeDifference.Hours() / 24)
			sender := mailv2.NewMessage()
			sender.SetHeader("From", header.Get("From"))
			sender.SetHeader("To", header.Get("To"))
			replyTitle := fmt.Sprintf("[重要] 资产确认[%v] %v", keyWord, nowTimeFormat)
			sender.SetHeader("Subject", replyTitle)

			// 设置 In-Reply-To 和 References 头字段
			sender.SetHeader("In-Reply-To", header.Get("Message-ID"))
			sender.SetHeader("References", header.Get("Message-ID"))

			htmlBody := fmt.Sprintf(`
	<head>
  <style>
    table {
      border-collapse: collapse;
      border-spacing: 0;
    }

    th, td {
      border: 1px solid black;
      padding: 8px;
      text-align: left;
    }
  </style>
</head>
	<p>各位领导:</p>

	<p>请确认以下资源的是否还在使用。</p>
	<p><b>截止时间: %v</b></p>
	<p><b>剩余时间: %v</b></p>
	<p><b>资源确认链接: https://xxxx/resource/list</b></p>
	<p><b>业务产品线: 售后</b></p>
	<table>
  <thead>
    <tr>
      <th>资源类型</th>
      <th>已确认</th>
      <th>待确认</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>ECS</td>
      <td>OSS</td>
      <td>RDS</td>
    </tr>
    <tr>
      <td>100</td>
      <td>120</td>
      <td>80</td>
    </tr>
    <tr>
      <td>30</td>
      <td>20</td>
      <td>10</td>
    </tr>
  </tbody>
</table>
	<p>烦请尽快确认资源归属</p>
	<p>感谢您的支持</p>
							`, deadLineFormat, daysDifference)
			sender.SetBody("text/html", htmlBody)
			d := mailv2.NewDialer("smtp.xx.cn", 587, "xxx@mail.com", "password")

			// 发送邮件
			if err := d.DialAndSend(sender); err != nil {
				panic(err)
			}
			fmt.Println("邮件发送成功!")
		}
	}

	fmt.Println("Done!")
}
个人公众号, 分享一些日常开发,运维工作中的日常以及一些学习感悟,欢迎大家互相学习,交流

在这里插入图片描述

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值