valine评论触发邮件通知

之前总结了在Hugo中如何加入valine来实现第三方评论插件,可以回顾《Hugo中加入Valine评论功能》,但希望Hugo上有评论时能够有一个通知的机制,这里就以邮件通知为例,基于之前介绍的Valine评论功能,进一步介绍如何实现Valine的评论通知。

总体思路是基于LeanCloud提供的API,来获取Valine存储的comments信息,通过计算获取最新的comments,然后发送通知。其实通知可以用任何方式,这里用比较简单的发送邮件来举例。

LeanCloud上绑定域名

因为要使用LeanCloud的API,基于LeanCloud的要求,必须绑定一个自己的域名。所以需要几个步骤

  • 在LeanCloud账户上的 设置=>域名绑定 中的API访问域名,按照设置步骤设置一个二级域名,比如可以是Hugo网站域名的二级域名;
  • 在DNS解析上增加一个CNAME配置(会提示如何配置);
  • 等待一段时间。需要经过域名配置检查、备案审核、SSL分配的过程,最后变成已绑定的状态;

使用LeanCloud的SDK访问API

这里以Python的SDK举例,参考https://leancloud.cn/docs/sdk_setup-python.html。除了Python之外,也有很多其它编程语言的SDK可以使用,这里使用Python主要是考虑简单。

可以创建Python的virtualenv来管理后续的Python代码,也可以直接在全局环境下操作,但是建议使用virtualenv管理代码和运行。

安装Python的SDK库

pip install leancloud

安装Python的pytz库,主要是比较datetime时间,由于LeanCloud返回的datetime数据包含timezone,所以需要做一个转换,用到pytz库

pip install pytz

核心代码

import leancloud
from datetime import datetime
import pytz

## 这里用一个本地的文件保存上次最后获取的comment的时间戳
def get_pos_from_file():
    with open("./comment_pos.txt", "r") as f:
        line = f.readline()
        ## 文件中的第一行,可以手动收入:2020-10-01 12:00:01这种
        ## 但是由于LeanCloud获取的comment时间数据是包含市区的,所以为了后面比较大小,这里也转换成有时间戳的datetime
        pos = datetime.strptime(line, "%Y-%m-%d %H:%M:%S").replace(tzinfo=pytz.timezone('UTC'))
        f.close()
        return pos

## 处理完之后更新时间戳
def set_pos_to_file(pos):
    with open("./comment_pos.txt", "w+") as f:
        pos = pos.strftime("%Y-%m-%d %H:%M:%S")
        f.write(pos)
        f.close()

## 获取最新的comments
def get_new_comments(update_time):
    leancloud.init("{你的LeanCloud账户的中APPID}", "{你的LeanCloud账户的中APP KEY}")
    ## 这里comments是配置放在LeanCloud的Comment结构中
    Comment = leancloud.Object.extend('Comment')
    query = Comment.query
    ## 查询大于某个时间的所有comments
    query.greater_than('updatedAt', update_time)
    items = query.find()
    return items


if __name__ == '__main__':
    tm = get_pos_from_file()
    comments = {}
    new_pos = tm
    ret_leancloud = get_new_comments(tm)

    ## 如果没有新的comments,则不做处理
    if len(ret_leancloud) == 0:
        exit(0)

    for comment in ret_leancloud:
        url = comment.get('url')
        content = comment.get('comment')
        updateTm = comment.get('updatedAt')
        if updateTm > new_pos:
            new_pos = updateTm

        if url in comments:
            comments[url].append(content)
        else:
            comments[url] = [content]

    ### 这里SendEmail,是自己实现发邮件,拼装邮件内容,这行代码不能直接copy,只是一个sample
    ### SendEmail(comments)
    
    set_pos_to_file(new_pos)

这段代码可以放在crontab中运行,即可完成定期查找最新comments并邮件通知的功能。

Python发送邮件

这里简单提一下发邮件,以使用163的smtp服务为例子(其它比如qq也都一样)。

首先先要获取一个邮箱账户的授权码,基本上现在主流邮箱都有,在开启smtp功能时,获取授权码,这个适用于smtp服务的登录,避免直接使用邮箱帐号和密码。

# coding: utf-8

import smtplib
from email.mime.text import MIMEText


class SmtpEmail:
    def __init__(self):
        self.host = 'smtp.163.com'
        self.port = 465
        self.username = '{你的邮箱帐号}'
        self.password = '{你的授权码}'
        self.sender = '{你的邮箱帐号}'  ## 邮件的发送人,一般要求跟username一样,否则会报错
        self.smtp = None
        self.isLogin = False

    def login(self):
        self.smtp = smtplib.SMTP_SSL(self.host, self.port)
        self.smtp.login(self.username, self.password)
        self.isLogin = True

    def sendEmail(self, receivers, title, content):
        if not self.isLogin:
            self.login()

        ## 这里使用html格式,即content可以是一段html代码。也可以用plain格式
        message = MIMEText(content, 'html', 'utf-8')
        message['Subject'] = title
        message['From'] = self.sender
        message['To'] = ",".join(receivers)
        self.smtp.sendmail(self.sender, receivers, message.as_string())


if __name__ == '__main__':
    smtp = SmtpEmail()
    smtp.sendEmail()