Chapter 11 如何科学的发送邮件

无论是数据分析还是运维监控,最终的结果通常都会通过Email的形式来展示,girlfriend也对Email做了很好的支持,通过使用内置的send_mail插件可以满足绝大多数的邮件发送场景。

配置

使用send_mail插件最先要做的事情就是配置smtp服务器,例如:

[smtp_test]
host=smtp.163.com
port=25
[email protected]
password=123456

接下来就可以在工作流中以test这个名称来引用这个smtp服务器的连接对象了。

如果你希望以ssl加密的方式进行传输,那么只需要修改端口号以及增加一个ssl为true的配置项就可以了:

[smtp_test]
host=smtp.163.com
port=465
[email protected]
password=123456
ssl=true

基本使用

如果只是发送一封包含标题和正文的邮件,那么非常简单:

Job(
    name="send_mail",
    args={
        "server": "test",
        "receivers": "[email protected]",
        "sender": "[email protected]",
        "subject": u"生日快乐!",
        "content": u"生日快乐!小泽同学!"
    }
)

如果要群发呢?群发分为两种情况,一种是发送给多个人,并且能在收件人中看到所有收件人的地址;另一种是虽然发送给多个人,但是每个人的收件人只能看到自己或者同组。send_mail对这两种方式都做了支持。

第一种:

from email.utils import COMMASPACE

...

Job(
    name="send_mail",
    args={
        "server": "test",
        "receivers": COMMASPACE.join([
            "[email protected]",
            "[email protected]",
            "[email protected]"
        ]),
        "sender": "[email protected]",
        "subject": u"明天开会,别忘了早起",
        "content": u"迟到的要给大家买好吃的啊!",
    }
)

这样[email protected]收到了邮件后,会在收件人列表中同时看到[email protected][email protected],这种情形适合于多人讨论的邮件,比如用户反馈之类。

第二种:

Job(
    name="send_mail",
    args={
        "server": "test",
        "receivers": [
            "[email protected]",
            "[email protected]",
            "[email protected]"
        ],
        "sender": "[email protected]",
        "subject": lambda ctx, receiver: u"你好," + receiver
        "content": lambda ctx, receiver: receiver + u",您的本月薪资是xxx"
    }
)

receivers参数变成了一个列表,然后subject和content接受了函数对象,这样可以针对不同的收件人进行不同的内容渲染。通过这种方式,每个人收到的邮件都不同,并且在收件人处只能看到自己的地址。如果内容相同,subject和content也是可以使用字符串的。

这种场景非常适合工资单,看看你们的财务MM还在手发工资单吗?那你只需要使用read_excelhtml_tablesend_mail这三个插件为她开发一个自动的工资单发送程序了,当然,前提是她要对你有足够的信任。

发送附件

一提到发送附件,你可能会想到需要层层组装的MIME对象,send_mail对附件做了适当的抽象,你不必关心任何的MIME层次,只需要关心要发送的文件即可。


from girlfriend.plugin.mail import Attachment

...

Job(
    name="send_mail",
    args={
        "server": "test",
        "receivers": "[email protected]",
        "sender": "[email protected]",
        "subject": u"本月的运营报表",
        "content": u"附件中,请查收",
        "attachments": [
            Attachment("opt_report.xlsx", "application/octet-stream", u"2016-03份运营报表.xlsx".encode("gb2312")),
        ]
    }
)

对,重点就是Attachment对象,每个Attachment代表着一个附件文件,Attachment接受三个参数,分别是要作为附件的文件路径、文件的MIME类型、附件要向收件人展示的文件名。

如果有多个附件,那就在列表中添加多个Attachment对象吧。

同时Attachment还保留了一个小惊喜,第一个参数除了接受字符串表示的文件路径之外,还可以接受一个StringIO对象,这意味着你可以把内存中的一些数据作为附件直接发出去,而不必先去创建文件,再读取,再发送附件。

Attachment(StringIO("too simple!"),
    "text/plain", u"文本.txt".encode("gb2312")),

自定义发送

虽然subject和content参数可以接受函数对象,但有时我们需要更加灵活的自定义,比如很多公司喜欢每天发送数据日报,日报中包含的数据和附件往往都是因人而异的,send_mail允许你对这类场景自己去定义邮件模板,所谓的邮件模板,就是一个Mail对象,包含了很多未实现的属性,需要你自己去实现这些属性。

class MyMail(Mail):

    def __init__(self, context, receiver):
        super(MyMail, self).__init__(context, receiver)

    @property
    def sender(self):
        return "[email protected]"

    @property
    def receiver_email(self):
        return self._receiver.email

    @property
    def subject(self):
         return u"新年快乐"

    @property
    def content(self):
         return u"新年快乐,发大财!{}".format(self._receiver.name)

    @property
    def attachments(self):
         return [
             Attachment(StringIO("simple!"),
                               "text/plain", u"文本.txt".encode("gb2312")),
             Attachment(StringIO("naive!"),
                               "text/plain", u"文本2.txt".encode("gb2312"))
         ]

class User(object):

    def __init__(self, name, email):
        self.name = name
        self.email = email

...


Job(
    name="send_mail",
    args={
        "server": "test",
        "receivers": [
                User(u"迟宏泽", "[email protected]"),
                User(u"小白", "[email protected]")
       ],
       "mail": MyMail
)

可以看到,现在receivers接受的是自定义的User对象,这意味着你可以直接将数据库中读取的对象作为receivers参数,MyMail中的构造方法会接受每次的User对象,并构建出一个新的MyMail对象,你可以基于User对象对MyMail中的各种属性进行灵活渲染。

其它

send_mail并不会在应用的整个生命周期中一直保持SMTP连接,它会在每次调用插件时创建连接和会话,发送完毕之后会将连接释放。如果你要想减少创建连接和会话的次数,应该在一次Job执行时尽可能发送相对多的邮件。

results matching ""

    No results matching ""