EmailBot
是一个 Python3 开发的邮件机器人框架/组件库,提供邮件收发功能,并能够根据规则对收到的邮件进行多维度的匹配,执行对应的功能;提供轻量化的服务支持。
日常工作中往往需要提供一些轻量化的服务,比如每日消息推送、固定化的流程审批、对某用户提供定向的数据拉取服务;简单的需求可以用脚本配合邮件推送来完成,比较复杂的需求我们可能会考虑搭建一个 web 服务来完成。
不过这类需求往往算不上一个服务,想在需求和实现之间寻找一个平衡点;起初想使用聊天机器人来搭个框架,不过想到邮箱使用范围更广,所以想用 EmailBot
来试试。
在使用 EmailBot
之前,需要首先确认邮箱服务开启了客户端访问(SMTP/POP3/IMAP),并且需要在邮箱服务里配置客户端密码,这和邮箱账户密码不同,需要手动进行配置获取,用于 EmailBot
登录邮箱;
EmailBot
可以作为命令行直接启动,或作为第三方库进行集成。
1.命令行启动
EmailBot
提供了命令行启动入口,可通过如下方式启动:
# python3 emailbot.py -h
usage: emailbot.py [-h] [-u USERNAME] [-p PASSWORD] [--smtp SMTP] [--smtpssl SMTPSSL]
[--pop3 POP3] [--pop3ssl POP3SSL] [-v]
EmailBot launch arguments as service
optional arguments:
-h, --help show this help message and exit
-u USERNAME, --username USERNAME
the emailbox username
-p PASSWORD, --password PASSWORD
the emailbox password
--smtp SMTP SMTP server address(address:port)
--smtpssl SMTPSSL connect SMTP server with ssl
--pop3 POP3 POP3 server address(address:port)
--pop3ssl POP3SSL connect POP3 server with ssl
-v, --version print emailbot version
如果未设置命令行参数,会尝试加载
config.py
中对应的配置,当然也可以将配置直接填写在该文件中。
EmailBot
仅是个框架,实际运行前需要配置自己的业务逻辑;在 interact.py
中编写规则识别函数和对应的处理逻辑,然后通过 INTERACTS
或 add_rule()
进行注册:
# interact.py
INTERACTS = {
# work_name: [Rule]
"debug": rule.Rule(callback_debug_show),
}
# emailbot.py
eb = EmailBot(...)
eb.login(...)
eb.add_rule(callback_debug_show, sender="[email protected]")
eb.run()
2.作为第三方库使用
在项目做添加 EmailBot
源码作为子目录,在项目中引入即可,如下:
emailbot = EmailBot(smtp="smtp.exmail.qq.com" --pop3="pop.exmail.qq.com")
status = emailbot.login(username, password)
if not status:
return
emailbot.add_rule(callback)
emailbot.run(daemon=True)
当然配置也可以直接写到
config.py
中。
可在项目中实现接收到新邮件后的规则匹配,以及最终执行的操作 callback
函数,然后通过 emailbot.add_rule()
加载,如下:
def callback_debug_show(email, regx):
print("callback_debug_show()")
print("Email=>")
print(email)
print("Regx=>")
print(regx)
# end callback_func()
Ubuntu20.04 x64
Python3
无第三方依赖
1.Class EmailBot
@Function: __init__(self, smtp="", pop3="", smtp_port=0, pop3_port=0,
smtp_ssl=False, pop3_ssl=False)
@Description: the EmailBot object initialize
@Parameter: smtp="", the SMTP server address, using configure if empty
@Parameter: pop3="", the POP3 server address, using configure if empty
@Parameter: smtp_port=0, the SMTP server port, using configure if empty
@Parameter: pop3_port=0, the POP3 server port, using configure if empty
@Parameter: smtp_ssl=False, ssl is required to connect to the SMTP,
using configure if empty
@Parameter: pop3_ssl=False, ssl is required to connect to the POP3,
using configure if empty
@Return: None
@Function: login(self, username="", password="")
@Description: initialize user/pass and server status, and check status
@Parameter: username="", the mailbox username, using configure if empty
@Parameter: password="", the mailbox password, using configure if empty
@Return: status, all server and user/pass is ready
@Function: add_rule(self, callback, sender="", subject="", content="", func=None)
@Description: add a rule for matching receive new email, when all the rules
are empty, it means all match.
@Parameter: callback, when the rule is successfully matched, the callback
function that needs to be executed
@Parameter: sender="", regexp rule which match sender
@Parameter: subject="", regexp rule which match subject
@Parameter: content="", regexp rule which match content
@Parameter: func=None, custom rule match function
@Return: None
@Function: send_email(self, to, cc="", subject="", content="", attachment="", blocking=False)
@Description: the user calls this function to send email.
if blocking=True, the email will be added to the queue to be sent, the
email will auto sent and retry.
if blocking=False, the email will send directly, and return send result
@Parameter: to, the email receiver
@Parameter: cc="", the email carbon copy
@Parameter: subject="", the email subject
@Parameter: content="", the email content
@Parameter: attachment="", the email attachment file path
@Parameter: blocking=False, blocking or not send mode
@Return: None
@Function: run(self, daemon=False)
@Description: the emailbot launch entrypoint
@Parameter: daemon=False, set background running, facilitate that emailbot
can be run as a service or be called as a library.
@Return: None
2.rule && callback
(可参考源码 interact.py
中的示例)
自定义规则函数接口规范如下:
@Function: rule_name(email)
@Description: the rule, xxxx
@Parameter: email, the Email object include email content
@Return: (match, regx), return match result, and return extracted fields as dictionary
回调函数接口规范如下:
@Function: callback_name(email, regx)
@Description: the callback, xxxx
@Parameter: email, the Email object include email content
@Parameter: regx, the dict that include result matched by rule
@Return: None
回调函数添加完毕后,可以直接在该文件下的 INTERACTS
中进行注册,或者通过 add_rule()
进行注册。
EmailBot
整体分为两个模块:1.邮件发送模块,2.邮件接收模块;如下:
邮件发送模块维护了一个待发送队列,当用户发送邮件时,将邮件加入到该队列中,由模块周期性的扫描并发送邮件,当邮件发送失败时,发送模块将自动重试,直至发送成功。(EmailBot
中 login()
函数会首先执行服务检查和用户名密码检查,所以在登录成功的情况下,其他的错误如网络故障,服务调整等都认为是可恢复的)
邮件接收模块通过获取邮件列表的 hash 值,来对比是否有新邮件到达,当新邮件达到后,将根据邮件的各项值(如:发件人,邮件名,内容)进行预设的规则匹配,当匹配成功后,调用对应的回调函数,执行具体功能。
.
├── CHANGELOG
├── Images
├── README.md
├── __init__.py
├── config.py EmaiBot配置文件
├── emailbot.py EmailBot主类实现和API
├── interact.py 用户自定义的规则和回调函数
├── mime.py email类实现以及MIME格式解析
├── protocol.py 邮件协议封装实现
├── rule.py 规则类的实现和匹配执行
└── utils.py 工具函数
EmailBot
判断是否接收到新邮件采用了传统邮件客户端的 hash
对比方法,也就是通过 uidl()
指令获取所有邮件的 hash 值,并比较新老列表就可以判断新邮件。(不能直接用收件箱总数进行判断,删除邮件或设置客户端接收邮件时间范围,都会引起总数的改变)。
建议使用时设置客户端接收邮件时间范围(如:腾讯企业邮箱默认设置1个月),目前
EmailBot
每次获取全量的邮件 hash 列表,如果收件箱邮件太多,且又没有设置收件时间范围的话,会导致框架执行较慢、造成额外的网络开销。
EmailBot
中的规则提供了简易正则模式和自定义模式,添加规则的方式如下:
add_rule(self, callback, sender="", subject="", content="", func=None)
callback: 规则匹配成功时,需要执行的回调函数
sender: 正则匹配邮件发送者
subject: 正则匹配邮件标题
content: 正则匹配邮件内容
func: 自定义规则匹配函数
在多个条件之间使用「AND」关系。在规则匹配时,会将匹配内容以 字典dict
的方式保存下来,传入到 callback
函数中,便于使用。
自定义规则匹配函数应按照如下规范进行编写:
@Function: rule_name(email)
@Description: the rule, xxxx
@Parameter: email, the Email object include email content
@Return: (match, regx), return match result, and return extracted fields as dictionary
该函数返回值需要返回匹配结果(bool
)和匹配字典(dict
),字典中包含提取的关键词,便于 callback
函数直接使用,EmailBot
会对所有规则的字典进行合并,如下:
{
"sender": "[email protected]",
"subject": "test",
"content": "this is test",
"name": "aaaa",
"id": 1234
}
回调函数接口规范如下,参数为邮件对象和规则函数匹配到的字典:
@Function: callback_name(email, regx)
@Description: the callback, xxxx
@Parameter: email, the Email object include email content
@Parameter: regx, the dict that include result matched by rule
@Return: None
当规则命中后,Emailbot
将启动新的线程执行该回调函数。
- 不支持附件接收
- 不支持 IMAP 协议
- email格式解析不完善,比如:plain 和 html 格式未区分
- 借助 mutt 来实现邮件自动化可能是更好的方案
References:
https://www.ietf.org/rfc/rfc1939.txt
https://www.ietf.org/rfc/rfc2060.txt
https://www.ietf.org/rfc/rfc2821.txt
https://docs.python.org/3/library/email.html
https://docs.python.org/3/library/smtplib.html
https://docs.python.org/3/library/poplib.html
https://docs.python.org/3/library/imaplib.html
https://github.com/python/cpython/blob/3.9/Lib/poplib.py
https://github.com/python/cpython/blob/3.9/Lib/smtplib.py
https://en.wikipedia.org/wiki/MIME
https://www.dev2qa.com/python-parse-emails-and-attachments-from-pop3-server-example/