├── .gitignore ├── README.md ├── __init__.py ├── account.json ├── com_poller.py ├── get_com.py ├── requirements.txt └── watch.py /.gitignore: -------------------------------------------------------------------------------- 1 | /account.json 2 | /config.json 3 | /.idea/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## 注意 3 | 4 | 插件后续将继续在 github 更新,欢迎提交 isuue 和 request 5 | 6 | ※ 提交issue反馈bug麻烦提供日志,并说明清楚 7 | 8 | > 注: 9 | 你现在可以在不同群监控不同的仓库了,在哪监控的就只会在哪推送,人人均可监控,每个人均可监控多个,但每个群要推送消息记得先开启github_reminder_poller服务哦 10 | 11 | ## 更新日志 12 | 13 | 24-01-18 v2.0.1 修复新版commit获取BUG,By [iona-s](https://github.com/iona-s),[issue #12](https://github.com/azmiao/github_reminder/issues/12), [pull #12](https://github.com/azmiao/github_reminder/pull/13) 14 | 15 | 22-07-08 v2.0.0 删了原来的垃圾代码,重写了一份,因此需要删除`config.yml`文件再更新,且需要重新监控仓库,试图解决[issue #8](https://github.com/azmiao/github_reminder/issues/8) 16 | 17 | 22-03-21 v1.4.1 全部改为异步进行,[issue #6](https://github.com/azmiao/github_reminder/issues/6) 18 | 19 | 21-11-09 v1.4 跟随github源码更新修复一个小bug,(github居然偷偷改源码了23333) 20 | 21 | 21-09-14 v1.3 修复string中含有其他代码引发的报错(issue #1),并增加了查仓库issue编号的判断 22 | 23 | 21-09-06 v1.2 应该修复了可能进程卡死的问题 24 | 25 | ## github_reminder 26 | 27 | 一个适用hoshinobot的 github仓库更新提醒 插件 28 | 29 | ## 项目地址: 30 | 31 | https://github.com/azmiao/github_reminder 32 | 33 | ## 功能 34 | 35 | ``` 36 | 命令如下: 37 | (链接支持github镜像站) 38 | [查仓库 仓库链接] 查该链接下的commits记录 39 | [监控仓库 仓库链接] 监控该仓库,推送功能需另外开启 40 | [不要监控仓库 仓库链接] 不再监控该仓库 41 | [查询监控仓库] 查询自己监控的仓库列表 42 | 43 | (自动推送监控的仓库更新,需要手动开启) 44 | ``` 45 | 46 | ## 简单食用教程: 47 | 48 | 1. git clone本插件: 49 | 50 | 在 HoshinoBot\hoshino\modules 目录下使用以下命令拉取本项目 51 | ``` 52 | git clone https://github.com/azmiao/github_reminder 53 | ``` 54 | 55 | 2. 设置代理(可选) 56 | 57 | 默认不启用代理! 58 | 59 | 打开 `account.json`:(默认状态) 60 | ``` 61 | { 62 | "proxy": {} 63 | } 64 | ``` 65 | 66 | 如果您的服务器在国内,那建议开启代理: 67 | 68 | 打开 `account.json`:(开启代理状态) 69 | 70 | 其中 1081 为代理的端口号,请自行查找你的代理的端口号并替换上 71 | ``` 72 | { 73 | "proxy": { 74 | "http": "http://localhost:1081", 75 | "https": "http://localhost:1081" 76 | } 77 | } 78 | ``` 79 | 80 | 2. 安装依赖,到HoshinoBot\hoshino\modules\github_reminder目录下,管理员方式打开powershell 81 | ``` 82 | pip install -r requirements.txt -i http://mirrors.aliyun.com/pypi/simple 83 | ``` 84 | 85 | 3. 在 HoshinoBot\hoshino\config\ `__bot__.py` 文件的 MODULES_ON 加入 'github_reminder' 86 | 87 | 然后重启 HoshinoBot 88 | 89 | 4. 在某一群内发送 “开启 github_reminder_poller” 即可启用自动推送功能 90 | 91 | 群内发送 'github推送帮助' 可获取更多帮助 -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from .get_com import create_msg 5 | from .watch import watch_depo, unwatch_depo, query_depo, current_dir 6 | from .com_poller import poller_update 7 | from hoshino import Service, get_bot 8 | 9 | # 首次启动不存在就创建文件 10 | if not os.path.exists(current_dir): 11 | with open(current_dir, 'w', encoding="UTF-8") as f: 12 | json.dump({}, f, indent=4, ensure_ascii=False) 13 | 14 | sv_help = '''命令如下: 15 | (链接支持github镜像站) 16 | 17 | [查仓库 仓库链接] 查该链接下的commits记录 18 | 19 | [监控仓库 仓库链接] 监控该仓库,推送功能需另外开启 20 | 21 | [不要监控仓库 仓库链接] 不再监控该仓库 22 | 23 | [查询监控仓库] 查询自己监控的仓库列表 24 | '''.strip() 25 | 26 | # 默认对所有群关闭推送 27 | sv = Service('github_reminder', help_=sv_help, enable_on_default=True) 28 | svup = Service('github_reminder_poller', enable_on_default=False) 29 | 30 | #帮助界面 31 | @sv.on_fullmatch('github推送帮助') 32 | async def help(bot, ev): 33 | await bot.send(ev, sv_help) 34 | 35 | # 直接查询某仓库 36 | @sv.on_prefix('查仓库') 37 | async def search_depo(bot, ev): 38 | url = str(ev.message) 39 | try: 40 | msg = await create_msg(url) 41 | except Exception as e: 42 | msg = f'查询失败,错误:{e}' 43 | await bot.send(ev, msg) 44 | 45 | # 监控 46 | @sv.on_prefix('监控仓库') 47 | async def watch_dep(bot, ev): 48 | gid, uid, url = str(ev.group_id), str(ev.user_id), str(ev.message) 49 | try: 50 | msg = await watch_depo(gid, uid, url) 51 | except Exception as e: 52 | msg = f'监控失败,错误:{e}' 53 | await bot.send(ev, msg) 54 | 55 | # 取消监控 56 | @sv.on_prefix('不要监控仓库') 57 | async def unwatch_dep(bot, ev): 58 | gid, uid, url = str(ev.group_id), str(ev.user_id), str(ev.message) 59 | msg = await unwatch_depo(gid, uid, url) 60 | await bot.send(ev, msg) 61 | 62 | # 查询监控仓库 63 | @sv.on_fullmatch('查询监控仓库') 64 | async def query_watched(bot, ev): 65 | gid, uid = str(ev.group_id), str(ev.user_id) 66 | msg = await query_depo(gid, uid) 67 | await bot.send(ev, msg) 68 | 69 | # 推送commits更新 70 | @svup.scheduled_job('cron', minute='*/5') 71 | async def depo_commit_poller(): 72 | bot = get_bot() 73 | try: 74 | await poller_update(bot) 75 | except Exception as e: 76 | svup.logger.info(f'检测commits更新失败,错误:{e}') -------------------------------------------------------------------------------- /account.json: -------------------------------------------------------------------------------- 1 | { 2 | "proxy": {} 3 | } -------------------------------------------------------------------------------- /com_poller.py: -------------------------------------------------------------------------------- 1 | import json 2 | import asyncio 3 | from datetime import datetime 4 | 5 | from .get_com import get_commits 6 | from .watch import current_dir 7 | from hoshino import logger 8 | 9 | # 判断是否有更新并推送 10 | async def poller_update(bot): 11 | with open(current_dir, 'r', encoding="UTF-8") as f: 12 | file_data = json.load(f) 13 | for gid in list(file_data.keys()): 14 | for uid in list(file_data[gid].keys()): 15 | if not file_data[gid][uid]: continue 16 | msg = f'[CQ:at,qq={uid}]\n' 17 | for url in list(file_data[gid][uid].keys()): 18 | await asyncio.sleep(0.5) 19 | old_time = datetime.strptime(file_data[gid][uid][url],"%Y-%m-%d %H:%M:%S") 20 | data_list = await get_commits(url) 21 | new_time = data_list[0]['time'] 22 | if new_time <= old_time: continue 23 | msg += f'\n◎您监控的仓库{url}检测到如下commit更新:' 24 | logger.info(f'检测到群{gid}的{uid}监控的仓库{url}有更新') 25 | # 该URL更新了 26 | for data in data_list: 27 | data_time = data['time'] 28 | if data_time > old_time: 29 | msg += f'\n▲{data["time"]} {data["author"]}提交了 "{data["title"]}"' 30 | # 替换监控信息的时间为最新commit时间 31 | file_data[gid][uid][url] = str(new_time) 32 | if msg != f'[CQ:at,qq={uid}]\n': 33 | await bot.send_group_msg(group_id=gid, message=msg) 34 | with open(current_dir, 'w', encoding="UTF-8") as f: 35 | json.dump(file_data, f, indent=4, ensure_ascii=False) -------------------------------------------------------------------------------- /get_com.py: -------------------------------------------------------------------------------- 1 | import json 2 | import os 3 | import re 4 | from datetime import datetime, timedelta 5 | 6 | import requests 7 | from bs4 import BeautifulSoup 8 | 9 | 10 | # 调整太平洋时间 11 | async def change_time(raw_time): 12 | raw_time = str(raw_time).replace('Z', '') 13 | txt_fmt = raw_time[:10] + " " + raw_time[11:19] 14 | dt = datetime.strptime(txt_fmt, "%Y-%m-%d %H:%M:%S") 15 | cur_time = dt + timedelta(hours=8) 16 | return cur_time 17 | 18 | 19 | # 返回commits的信息 20 | async def get_commits(url): 21 | # 获取真实URL 22 | url_tmp = re.match(fr'(https://github.com/\S+/\S+/)tree(/\S+)', url) 23 | url = url + '/commits' if not url_tmp else url.replace('tree', 'commits') 24 | # 读取代理 25 | with open(os.path.join(os.path.dirname(__file__), 'account.json')) as fp: 26 | p_info = json.load(fp) 27 | proxy = p_info['proxy'] 28 | data_list = [] 29 | resp = requests.get(url=url, proxies=proxy, timeout=20) 30 | soup = BeautifulSoup(resp.text, 'lxml') 31 | soup_find = soup.find('script', {'data-target': 'react-app.embeddedData'}) 32 | raw_info = json.loads(soup_find.text) 33 | commit_info_list = raw_info['payload']['commitGroups'] 34 | for commit_info_by_date in commit_info_list: 35 | for commit_info in commit_info_by_date['commits']: 36 | if len(data_list) >= 5: # 如果要只取最近5次commit 37 | break 38 | commit_message = commit_info['shortMessage'] 39 | authors_name = ','.join([info['displayName'] for info in commit_info['authors']]) 40 | time = await change_time(commit_info['committedDate']) 41 | data_list.append({'title': commit_message, 'author': authors_name, 'time': time}) 42 | return data_list 43 | 44 | 45 | # 生成消息文本 46 | async def create_msg(url): 47 | data_list = await get_commits(url) 48 | msg = f'仓库{url}最近5次commit如下:' 49 | for data in data_list: 50 | title = data['title'] 51 | author = data['author'] 52 | time = data['time'] 53 | msg += f'\n▲{time} {author}提交了 "{title}"' 54 | return msg 55 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | json 2 | datetime 3 | requests -------------------------------------------------------------------------------- /watch.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from .get_com import get_commits 5 | 6 | # 监控信息文件 7 | current_dir = os.path.join(os.path.dirname(__file__), 'config.json') 8 | 9 | # 监控 10 | async def watch_depo(gid, uid, url): 11 | with open(current_dir, 'r', encoding="UTF-8") as f: 12 | file_data = json.load(f) 13 | group_info = file_data.get(gid, {}) 14 | user_info = group_info.get(uid, {}) 15 | if user_info.get(url, {}): return '你已经监控过该仓库了,无需再次监控' 16 | data_list = await get_commits(url) 17 | priv_time = str(data_list[0]['time']) 18 | user_info[url] = priv_time 19 | group_info[uid] = user_info 20 | file_data[gid] = group_info 21 | with open(current_dir, 'w', encoding="UTF-8") as f: 22 | json.dump(file_data, f, indent=4, ensure_ascii=False) 23 | return f'您已成功在群{gid}里监控仓库:{url}' 24 | 25 | # 取消监控 26 | async def unwatch_depo(gid, uid, url): 27 | with open(current_dir, 'r', encoding="UTF-8") as f: 28 | file_data = json.load(f) 29 | group_info = file_data.get(gid, {}) 30 | user_info = group_info.get(uid, {}) 31 | if not user_info.get(url, {}): return '你还未监控过该仓库,请先使用命令监控' 32 | file_data[gid][uid].pop(url) 33 | with open(current_dir, 'w', encoding="UTF-8") as f: 34 | json.dump(file_data, f, indent=4, ensure_ascii=False) 35 | return f'您已成功在群{gid}里取消监控仓库:{url}' 36 | 37 | # 监控查询 38 | async def query_depo(gid, uid): 39 | with open(current_dir, 'r', encoding="UTF-8") as f: 40 | file_data = json.load(f) 41 | group_info = file_data.get(gid, {}) 42 | user_info = group_info.get(uid, {}) 43 | return '您暂未监控任何仓库呢!' if not user_info else '您所监控的仓库有:\n' + '\n'.join(list(user_info.keys())) --------------------------------------------------------------------------------