简单定时脚本的应用 发表于 2023-12-06 | 更新于 2024-04-18
| 字数总计: 2.1k | 阅读时长: 9分钟 | 阅读量:
这篇文章通过一个简单的 Python 脚本演示 Github API 的使用、Python 编写以及 Webhook 的调用。
以此为基础,你可以展开更多的想法:
实时监测 GitHub 的各项信息并推送;
NPM 包下载量监测;
LeetCode 每日一题;
排名监测;
获取某官网上的重要公告等等。
本文主要介绍:
获取用户 Github 上的 Star 数量并推送的脚本编写
根据用户名监测某作者 NPM 包年下载量
获取 Github 上的 Star 数量并推送
GitHub 应用注册
打开你的 GitHub 主页(profile)进入开发者选项:
选择「新建一个」GitHub APP。根据需要进行相关设置的填写。
生成密钥:
记住密钥,待会脚本需要调用。
脚本的编写
这里参考了 songquanpeng/scripts 的实现,并进行修复与改编。
这个 python 脚本(这里命名为 github_stars.py
)所做的工作:
调用 GitHub API(Repositories - GitHub Docs )
统计所有项目的 star 数量
与上次统计结果进行比较(结果保存在本地)
调用推送服务发送 POST JSON 格式的请求
执行脚本需要的三个参数:
GitHub 用户名
GitHub API 令牌
调用的推送服务(将发送 POST)请求
源代码【非面向对象版本】如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 import jsonimport requestsimport sysfilename = "./.star_count" push_url = "" username="" def star_counter (username: str , token: "" ) -> int : """ 调用GitHub API获取个人仓库信息,返回统计的stars数 :param username: :param token: :return: """ all_repos_url = f"https://api.github.com/users/{username} /repos?per_page=100" header = {} if token == "" else {"Authorization" : f"bearer {token} " } res = requests.get(all_repos_url, header) repos = res.json() count = 0 for repo in repos: count += repo["stargazers_count" ] return count def load_count () -> int : ''' 加载上一次的统计结果,若无则默认为0 :return: ''' try : with open (filename, 'r' ) as f: count = int (f.read()) except IOError: count = 0 return count def save_count (count: int ): ''' 保存统计结果 :param count: :return: ''' with open (filename, 'w' ) as f: f.write(str (count)) def send_message (msg: str ): """ 发送消息 :param msg: :return: """ headers = { "Content-Type" : "application/json; charset=UTF-8" } pyload = { "title" :"GitHub Stars" , "msg" :msg } response = requests.post(push_url,data=json.dumps(pyload),headers=headers).text print (response) def message_construct (last_count,current_count ) -> str : """ 自定义的信息构造函数。 样例:【每日检测】检测到用户uuanqin总star数增加 :) 。[0 -> 6, total: 6] :param last_count: :param current_count: :return: """ s = f"【每日检测】检测到用户{username} 总star数" if current_count > last_count: s += f"增加 :) 。" else : s += f"降低 :( 。" s += f"[{last_count} -> {current_count} , total: {current_count-last_count} ]" return s def main (): if len (sys.argv) != 4 : print ("Error! This script requires three arguments: GITHUB_USERNAME GITHUB_TOKEN PUSH_URL" ) return global push_url,username username = sys.argv[1 ] token = sys.argv[2 ] push_url = sys.argv[3 ] last_count = load_count() current_count = star_counter(username, token) if current_count != last_count: send_message(message_construct(last_count,current_count)) save_count(current_count) if __name__ == '__main__' : main()
注意,代码中不要使用奇奇怪怪的字符,比如 emoji 表情等,导致 utf-8 和 gbk 都不能识别。
这里我使用了自己的部署服务。使用示例:
1 python ./github_stars.py uuanqin 9xxxxxxxx7 https://push.uuanqin.top/webhook/8xxxxxxe
把脚本放在 Linux 服务器上,记得测试。
这里使用的 API 似乎有点老了,但是还能用。我在文档中没有找到确切 的对应文档说明。
定时执行
新建 cron 文件:
1 2 8 * * * /usr/bin/python /var/www/push_script/github_stars/github_stars.py uuanqin 9xxxxxxxxxxxxxx7 https://push.uuanqin.top/webhook/8xxxxxxxxxxxxxxe
添加定时任务:
1 crontab github_stars.cron
每天早上 8 点 02 分调用该脚本。
附 在线网站 验证结果:
不了解 cron 可以查看这篇文章:Linux 使用 cron 创建定时任务
像我的部署服务使用了飞书群机器人,推送效果如下:
想了解这个项目可以查看这篇文章:[[TBD]]
NPM 下载量的监测
根据上一章介绍的脚本,我们重构一下,改成面向对象的版本。以下脚本能同时支持 GitHub stars 数量的检测以及 NPM 年下载量的检测。注意,临时的数据文件存储在 root 的目录下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 import datetimeimport jsonimport osimport requestsimport sysfrom abc import ABC, abstractmethodfrom dateutil.relativedelta import relativedeltaclass Obj : filename = "" title = "" last_count = 0 current_count = 0 push_url = "" def __init__ (self,push_url ): self.push_url = push_url self.load_count() self.count() @abstractmethod def count (self ): pass @abstractmethod def message_construct (self ): pass def load_count (self ): ''' 加载上一次的统计结果,若无则默认为0 ''' try : with open (self.filename, 'r' ) as f: count = int (f.read()) except IOError: count = 0 self.last_count = count def save_count (self ): ''' 保存统计结果 ''' with open (self.filename, 'w' ) as f: f.write(str (self.current_count)) def send_message (self, msg: str ): """ 发送消息 """ headers = { "Content-Type" : "application/json; charset=UTF-8" } pyload = { "title" : self.title, "msg" : msg } response = requests.post(self.push_url, data=json.dumps(pyload), headers=headers).text print (response) def push (self ): current_count = self.current_count last_count = self.last_count if current_count != last_count: self.send_message(self.message_construct()) self.save_count() class GitHubStars (Obj ): username = "" github_token = "" def __init__ (self,push_url,token,username ): self.filename = os.path.join(os.getcwd(),".star_count" ) self.title = "GitHub Stars Count" self.github_token = token self.username = username super ().__init__(push_url) def count (self ): """ 调用GitHub API获取个人仓库信息,返回统计的stars数 """ all_repos_url = f"https://api.github.com/users/{self.username} /repos?per_page=100" header = {} if self.github_token == "" else {"Authorization" : f"bearer {self.github_token} " } res = requests.get(all_repos_url, header) repos = res.json() count = 0 for repo in repos: count += repo["stargazers_count" ] self.current_count = count def message_construct (self ) -> str : """ 自定义的信息构造函数。 样例:【每日检测】检测到用户uuanqin总star数增加 :) 。[0 -> 6, total: 6] """ s = f"【每日检测】检测到用户{self.username} 总star数" if self.current_count > self.last_count: s += f"增加 :) 。" else : s += f"降低 :( 。" s += f"[{self.last_count} -> {self.current_count} , total: {self.current_count-self.last_count} ]" return s class NpmAuthorDownloads (Obj ): username = "" def __init__ (self,push_url,username ): self.filename = os.path.join(os.getcwd(),".npm_count" ) self.title = "GitHub Stars Count" self.username = username super ().__init__(push_url) def count (self ): nowtime = datetime.datetime.now() yesterdate = nowtime - datetime.timedelta(days=+1 ) last_year = yesterdate - relativedelta(years=1 ) yesterdate_strf=yesterdate.strftime('%Y-%m-%d' ) last_year_strf=last_year.strftime('%Y-%m-%d' ) url = f"https://npm-stat.com/api/download-counts?author={self.username} &from={last_year_strf} &until={yesterdate_strf} " res = requests.get(url) repos = res.json() count = 0 for p,con in repos.items(): for d,v in con.items(): count += v self.current_count = count def message_construct (self ): """ 自定义的信息构造函数。 样例:【每日检测】检测到用户uuanqin总star数增加 :) 。[0 -> 6, total: 6] """ s = f"【每日检测】检测到用户{self.username} 的NPM账户去年包下载量发生变动。" s += f"[{self.last_count} -> {self.current_count} , total: {self.current_count - self.last_count} ]" return s def main (): if len (sys.argv) <2 : print ("Error! Arguments is illegal" ) return type = sys.argv[1 ] obj = None if type =="github_stars" and len (sys.argv) != 5 : print ("Error! This script requires Four arguments: TYPE GITHUB_USERNAME GITHUB_TOKEN PUSH_URL" ) return elif type =="npm_downloads" and len (sys.argv) != 4 : print ("Error! This script requires Four arguments: TYPE NPM_AUTHOR PUSH_URL" ) return elif type != "github_stars" and type != "npm_downloads" : print ("Error! Arguments is illegal" ) return if type == "github_stars" : obj = GitHubStars(sys.argv[4 ],sys.argv[3 ],sys.argv[2 ]) elif type == "npm_downloads" : obj = NpmAuthorDownloads(sys.argv[3 ],sys.argv[2 ]) obj.push() if __name__ == '__main__' : main()
cron 的其它应用
本文参考