NiceLeeのBlog 用爱发电 bilibili~

一种利用Github notification达到邮件通知目的的思路及其Python实现

2021-03-01
nIceLee

阅读:


说到邮件通知,简单的试了一下,发觉里面的坑蛮深的。
很多云服务直接把端口给封了。。。
邮件服务商也满满的套路,巴不得你用他们的app才好(海外一样)。
开启第三方SMTP服务各种身份验证。隔三岔五“你是不是本人?”
真正的spam拦也拦不住,尽给真正用户增加使用门槛。
抢市场的时候总是野蛮生长,当大头后根据相关规范!
有的时候还发现你的邮件躺在垃圾邮箱里。。。
得了,通知而已,让人代发吧。

前言

  • Github在什么时候会给我发邮件呢?
    • 我设置关注的repo发布了新的release
    • 我设置关注的repo有了新的issue
    • 我设置关注的repo有了新的pull request
    • 我设置关注的issue下有了新的回复
    • 我设置关注的pull request有了新的回复
    • 我设置关注的gist有了新的回复
    • 我参加并设置关注的team有了讨论
  • Github给我发邮件的标题/内容是什么呢?
    • 基本是事件主体的内容。
      因为需求是通知,控制正文的文本内容很容易达到。
      更复杂的,比如格式要求、附件、回执等这里就不做考虑了。
  • 隐私和安全性问题
    • 如果不考虑邮件通知的隐私问题,对象直接取用公开的repo即可。
    • 项目可以设为私有(由此可能引发实现的某些问题,但可以解决。)
    • Gist可以设为secret,但其内容仍然是对任何人可见的。
  • 关于实现的考量
    • 由于Github不可能发邮件提示你本人自己的操作,故而需要两个账号。
    • 由于个人私有项目无法对第二人可见,故而只能采用组织私有项目的形式。
      这需要再创建一个组织。

实现思路

  • 一种利用issue(comment)的实现思路
    • 准备账号A,该账号的注册邮箱用于接收邮件
    • 准备账号B,并生成用于github操作的token。该账号用于创建issue和issue comment
    • A(或者B)创建一个组织C,并邀请B(或者A)加入
    • A(或者B)在组织C创建一个私有的项目repo
    • 确认AB对repo的接入权限
    • 确认A对repo的关注
    • 接下来,考虑B如何在repo新建issue和issue comment即可
      • 先人为在repo创建一个issue
      • api查询最近的issue信息
        • 若最近的comment数量大于20,api新建一个issue
        • 若最近的comment数量小于等于20,在该issue下api新建一个issue comment

示例代码

#!/usr/bin/env python
# coding:utf-8
import os
import requests,json
import time

        
def load_from_env():
    repo_full_name = os.environ.get('MY_REPOSITORY')
    owner = os.environ.get('MY_OWNER')
    repo = repo_full_name[len(owner) + 1:]
    token = os.environ.get('MY_GITHUB_TOKEN')
    github_config = {
        "repo": repo,
        "owner": owner,
        "token": token,
    }
    return github_config
    
class GithubHelper:
    
    def __init__(self, owner, repo, token, **args):
        self.auth = token
        self.owner = owner
        self.repo = repo
        
    def getLatestRelease(self, **args):
        url = 'https://api.github.com/repos/%s/%s/releases/latest'%(self.owner, self.repo)
        headers = {'User-Agent': 'None', 'Accept': 'application/vnd.github.v3.+json', "Authorization": "token "+ self.auth}
        res = requests.get(url, headers=headers).json()
        #print(res)
        return res
    
                
    def updateReleaseBody(self, release_id, body, **args):
        url = 'https://api.github.com/repos/%s/%s/releases/%d'%(self.owner, self.repo, release_id)
        headers = {'User-Agent': 'None', 'Accept': 'application/vnd.github.v3.+json', "Authorization": "token "+ self.auth}
        param = {
            "body": body
        }
        res = requests.request("PATCH", url, data=json.dumps(param), headers=headers).json()
        #print(res)
        return res 
        
    def updateReleaseAsset(self, asset_id, name, **args):
        url = 'https://api.github.com/repos/%s/%s/releases/assets/%d'%(self.owner, self.repo, asset_id)
        headers = {'User-Agent': 'None', 'Accept': 'application/vnd.github.v3.+json', "Authorization": "token "+ self.auth}
        param = '{"name":"%s"}'%name
        res = requests.request("PATCH", url, data=param, headers=headers).json()
        #print(res)
        return res    
            
    def deleteReleaseAsset(self, asset_id, **args):
        url = 'https://api.github.com/repos/%s/%s/releases/assets/%d'%(self.owner, self.repo, asset_id)
        headers = {'User-Agent': 'None', 'Accept': 'application/vnd.github.v3.+json', "Authorization": "token "+ self.auth}
        res = requests.request("DELETE", url, headers=headers)
        #print(res.text)
        return res
        
    def uploadReleaseAsset(self, release_id:int, name, data, **args):
        url = 'https://uploads.github.com/repos/%s/%s/releases/%d/assets?name=%s'%(self.owner, self.repo, release_id, name)
        headers = {'content-type': 'application/octet-stream', 'Accept': 'application/vnd.github.v3.+json', "Authorization": "token "+ self.auth}
        res = requests.request("POST", url, data=data, headers=headers).json()
        #print(res)
        return res
        
    def replaceLatestReleaseAssets(self, name, data, **args):
        if not "releaseInfo" in locals():
            releaseInfo = self.getLatestRelease()
        release_id = releaseInfo['id']
        assets = releaseInfo['assets']
        # 删除原来的附件
        for asset in assets:
            if name == asset["name"]:
                self.deleteReleaseAsset(asset['id'])
                break;
        # 上传新的附件
        return self.uploadReleaseAsset(release_id, name, data)
        
            
    def createIssueComment(self, issue_id:int, content:str, **args):
        url = 'https://api.github.com/repos/%s/%s/issues/%d/comments'%(self.owner, self.repo, issue_id)
        data = {"body":content}
        headers = {
            'Accept': 'application/vnd.github.v3.+json', 
            'Content-Type': 'application/json; charset=utf-8', 
            "Authorization": "token "+ self.auth
        }
        #d = encode_multipart_formdata(data, boundary=boundary)        
        res = requests.request("POST", url, data = json.dumps(data), headers=headers).text
        print(res)
        return res
        
    def createIssue(self, title, body, **args):
        url = 'https://api.github.com/repos/%s/%s/issues'%(self.owner, self.repo)
        headers = {'User-Agent': 'None', 'Accept': 'application/vnd.github.v3.+json', "Authorization": "token "+ self.auth}
        param = {
            "title": title,
            "body": body,
        }
        res = requests.request("POST", url, data=json.dumps(param), headers=headers).json()
        print(res)
        return res
        
    def getLatestIssue(self, **args):
        url = 'https://api.github.com/repos/%s/%s/issues?per_page=1'%(self.owner, self.repo)
        headers = {'User-Agent': 'None', 'Accept': 'application/vnd.github.v3.+json', "Authorization": "token "+ self.auth}
        res = requests.request("GET", url, headers=headers).json()
        print(res)
        return res 
    
    def send_notice(self, content, **args):
        res = self.getLatestIssue()
        if isinstance(res, list) and len(res) <= 20:
            self.createIssueComment(res[0]["number"], content)
        else:
            self.createIssue('邮件通知issue[%d]'%issue["number"], content)
        
            
    def createGist(self, gist_id, content, **args):
        url = 'https://api.github.com/gists/%s/comments'%(gist_id)
        print(url)
        headers = {'User-Agent': 'None', 'Accept': 'application/vnd.github.raw', "Authorization": "token "+ self.auth}
        param = {
            "body": content,
        }
        res = requests.request("POST", url, data=json.dumps(param), headers=headers).json()
        print(res)
        return res
        
if __name__ == "__main__":
    github_config = load_from_env()
    """或者这样,repo是项目名,owner是组织名,token是B的tokan
    github_config = {
        "repo": repo,
        "owner": owner,
        "token": token,
    }
    """
    helper = GithubHelper(**github_config)
    #helper.getLatestRelease()
    #helper.updateReleaseAsset(asset_id = 123,name = "test.zip")
    #helper.uploadReleaseAsset(release_id = 123,name = "test.zip",data = 'hahaha')
    #helper.deleteReleaseAsset(asset_id = 123)
    #helper.replaceLatestReleaseAssets(name = "test.zip", data = '1234测试'.encode("utf-8"))
    #date_now = get_now_date_str()
    #body = '更新于%s'%date_now
    #helper.updateReleaseBody(release_id = 123, body=body)
    #helper.createIssueComment(123, "test @xxx")
    #helper.createIssue('邮件通知issue', "test @xxx")
    helper.send_notice("快点干活啦~~")


内容
隐藏