This commit is contained in:
623756217 2023-03-28 15:43:44 +08:00
parent 05c587ce76
commit 3c8544bdda
18 changed files with 487 additions and 0 deletions

0
bot.log Normal file
View File

Binary file not shown.

Binary file not shown.

31
commons/apiUtil.py Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/python
# 负责调用平台接口 完成各项操作
import sys
sys.path.append('..')
import requests
import json
import config.baseConfig as baseconfig
from commons.logUtil import logger
# 获取 pull状态新信息
def get_pull_infor(owner, repo, index):
url = baseconfig.apiUrl + "api/v1/{}/pulls/{}.json".format(repo, index)
response = requests.get(url, headers=baseconfig.header, proxies = baseconfig.proxies)
logger.info("get_pull_infor调用"+str(response.json()))
return response.json()
# 获取 添加pull评论信息
def create_pull_comment(issue_id):
url = baseconfig.apiUrl + "api/issues/{}/journals.json".format(issue_id)
COMMENT = "注意!\n该合并请求已创建满两小时,长时间未处理可能会降低贡献的质量和贡献者积极性。\n请及时处理!"
data = json.dumps({'content': COMMENT})
response = requests.post(url, data=data, headers=baseconfig.header, proxies = baseconfig.proxies)
logger.info("create_pull_comment调用"+str(response.json()))
return response.json()
if __name__ == '__main__':
get_pull_infor("wuxiaojun", "wuxiaojun/botreascrch", "3")

47
commons/authUtil.py Normal file
View File

@ -0,0 +1,47 @@
#!/usr/bin/python
# 负责调用平台接口 完成各项操作
import json
import sys
sys.path.append('..')
import jwt
import time
import sys
# 通过bot基本信息字段获取JWT 和 token
def getJWT():
if len(sys.argv) > 1:
pem = sys.argv[1]
else:
# pem = input("Enter path of private PEM file: ")
pem = 'C:\\Users\\文学奖\\Downloads\\my-first-apps1.2023-02-05.private-key (1).pem' # (1)
# Get the App ID
if len(sys.argv) > 2:
app_id = sys.argv[2]
else:
# app_id = input("Enter your APP ID: ")
app_id = 10009 #"CrnV5uYEmwMGu7H6osHHi-4aSyKdY99qRMw4i44Kfi4" #186702
# Open PEM
with open(pem, 'rb') as pem_file:
signing_key = jwt.jwk_from_pem(pem_file.read())
#signing_key = jwt.jwk_from_pem(privateKey)
payload = {
# Issued at time
'iat': int(time.time()),
# JWT expiration time (10 minutes maximum)
'exp': int(time.time()) + 600,
# GitHub App's identifier
'iss': app_id
}
# Create JWT
jwt_instance = jwt.JWT()
encoded_jwt = jwt_instance.encode(payload, signing_key, alg='RS256')
print(f"JWT: ", encoded_jwt)
if __name__ == "__main__":
print("hello")

57
commons/logUtil.py Normal file
View File

@ -0,0 +1,57 @@
#!/usr/bin/python
import logging
import logging.handlers
'''
日志工具类
'''
class Logging:
def __init__(self):
# log文件存储路径
self._log_filename = 'bot.log'
'''
%(levelno)s: 打印日志级别的数值
%(levelname)s: 打印日志级别名称
%(pathname)s: 打印当前执行程序的路径其实就是sys.argv[0]
%(filename)s: 打印当前执行程序名
%(funcName)s: 打印日志的当前函数
%(lineno)d: 打印日志的当前行号
%(asctime)s: 打印日志的时间
%(thread)d: 打印线程ID
%(threadName)s: 打印线程名称
%(process)d: 打印进程ID
%(message)s: 打印日志信息
'''
# 日志信息输出格式
self._formatter = logging.Formatter('%(asctime)s - %(process)d - '
'%(pathname)s - %(levelname)s: %(message)s')
# 创建一个日志对象
self._logger = logging.getLogger()
# 设置控制台日志的输出级别: 级别排序:CRITICAL > ERROR > WARNING > INFO > DEBUG
self._logger.setLevel(logging.INFO) # 大于info级别的日志信息都会被输出
self.set_console_logger()
self.set_file_logger()
def set_console_logger(self):
'''设置控制台日志输出'''
console_handler = logging.StreamHandler()
console_handler.setFormatter(self._formatter)
self._logger.addHandler(console_handler)
def set_file_logger(self):
'''设置日志文件输出'''
formatter = logging.Formatter('%(asctime)s - %(process)d - '
'%(pathname)s - %(levelname)s: %(message)s')
# 将输出日志信息保存到文件中
file_handler = logging.handlers.RotatingFileHandler(
self._log_filename, maxBytes=10485760, backupCount=5, encoding="utf-8")
file_handler.setFormatter(self._formatter)
self._logger.addHandler(file_handler)
def get_logger(self):
return self._logger
logger = Logging().get_logger()

Binary file not shown.

49
config/baseConfig.py Normal file
View File

@ -0,0 +1,49 @@
#!/usr/bin/python
# mysql 数据库配置信息
db_config = {
'host': 'localhost',
'user': '',
'password': '',
'database': 'pulldb',
'use_unicode': True,
'charset': 'utf8mb4'
}
# request 头部配置 目前token写死在头部正常需通过 api来获取有效token
header = {
'User-Agent': 'Mozilla/5.0',
'Authorization': 'Bearer token_str',
'Content-Type': 'application/json',
}
# app配置
host = "0.0.0.0"
port = 3090
# request 代理配置
proxies = {"http": None, "https": None}
# 平台接口url
apiUrl = "https://www.gitlink.org.cn/"
# payload = {
# "action":"closed",
# "number":2,
# "pull_request":{
# "id":3406,
# "url":"https://gitlink.org.cn/wuxiaojun/botreascrch/pulls/2",
# "number":2,
# "user":{
# "id":42378,
# "login":"wuxiaojun",
# },
# "created_at":"2023-03-22T21:36:54+08:00",
# },
# "repository":{
# "id":10203,
# "name":"botreascrch",
# }
# }

11
config/botConfig.py Normal file
View File

@ -0,0 +1,11 @@
#!/usr/bin/python
botId = 10009
clientId = "ENmlgAJo2FBsENZH0DE-xbS67bxOSyTjMPNRoi_pzXQ"
clientSecret = "xaMJ4Xyb4s5SAthe9ZCo6zX7LrB9cG6y-vrucEmXsIk"
pemPath = 'C:\\Users\\文学奖\\Downloads\\my-first-apps1.2023-02-05.private-key (1).pem'
token = "1XPMnNzTSS8s3hlYvHfw0wivKHJvtQLtzCt3mDRnOqs"

Binary file not shown.

View File

@ -0,0 +1,65 @@
#!/usr/bin/python
import sys
sys.path.append('..')
from flask import Flask, jsonify, request
from flask_cors import CORS, cross_origin
import json
import services.pullService as pullService
import datetime
from commons.logUtil import logger
app = Flask(__name__)
# 测试
@app.route('/hello', methods=['get'])
@cross_origin()
def hello():
return jsonify({
"code": 0,
"msg": "hello"
})
# 功能:每当有一个新的PR创建/关闭时,根据状态执行存储或更新操作
@app.route('/prwelcome', methods=['POST'])
@cross_origin()
def pr_welcome():
try:
payload = request.json
print(payload)
logger.info("获取webhook信息成功:" + str(payload))
# # 判断是新创建issue还是issue的状态发生改变
if payload["action"] == 'opened':
pull_id = payload["pull_request"]["id"]
index = payload["pull_request"]["number"]
time = payload["pull_request"]["created_at"][:19]
owner = payload["pull_request"]["user"]["login"]
repo = payload["repository"]["full_name"]
created = datetime.datetime.strptime(time, "%Y-%m-%dT%H:%M:%S")
check_time = created + datetime.timedelta(hours=2)
values = (pull_id, index, owner, repo, created, check_time, 0)
# print(values)
pullService.insert_pull(values)
# 关闭 更新数据库对应pr状态 (合并或者拒绝都属于关闭)
elif payload["action"] == 'closed':
index = payload["pull_request"]["number"]
owner = payload["pull_request"]["user"]["login"]
repo = payload["repository"]["full_name"]
pullService.update_pull(index, owner, repo)
return jsonify({
"code": 0,
"msg": "success"
})
except Exception as e:
return jsonify({
"code": -1,
"msg": "exception:" + str(e)
})
if __name__ == '__main__':
app.run(port=3090)

33
dbResource/pulldb.sql Normal file
View File

@ -0,0 +1,33 @@
/*
Navicat MySQL Data Transfer
Source Server : localhost
Source Server Version : 80023
Source Host : localhost:3306
Source Database : pulldb
Target Server Type : MYSQL
Target Server Version : 80023
File Encoding : 65001
Date: 2023-03-23 16:56:23
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for pull
-- ----------------------------
DROP TABLE IF EXISTS `pull`;
CREATE TABLE `pull` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键id',
`pullid` int NOT NULL COMMENT 'pr主键id',
`index` int DEFAULT NULL COMMENT 'pull下标',
`owner` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '仓库拥有者login',
`repo` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '仓库名称',
`created` datetime NOT NULL COMMENT 'pr创建时间',
`closed` datetime DEFAULT NULL COMMENT '关闭时间',
`checktime` datetime NOT NULL COMMENT 'pr预期有变化时间',
`status` int NOT NULL DEFAULT '0' COMMENT 'Pull状态0打开1 关闭2 提醒完成',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

3
entity/pull.py Normal file
View File

@ -0,0 +1,3 @@
#!/usr/bin/python
# 注意 number 和 id有什么区别

Binary file not shown.

40
jobs/tasks.py Normal file
View File

@ -0,0 +1,40 @@
#!/usr/bin/python
import sys
sys.path.append('..')
import datetime
from apscheduler.schedulers.blocking import BlockingScheduler
# 定时任务处理与调度
# 测试程序
def func():
now = datetime.datetime.now()
ts = now.strftime('%Y-%m-%d %H:%M:%S')
print('do func time :', ts)
# 扫描数据库获取未完成的到期pr执行相关操作
def check_pr():
# 查询数据库获取未完成的到期pr
# 循环遍历调用接口查询pr状态是否新增评论或者状态为关闭
# 对不活跃的pr调用接口发表评论提醒处理
return
# 定时执行任务
# 参数 func 定时函数 time 间隔时间min
def do_timer_job(func, time):
# 创建调度器BlockingScheduler
scheduler = BlockingScheduler()
# 添加任务,时间间隔5S
scheduler.add_job(func, 'interval', seconds=time, id='timer_job1')
scheduler.start()
if __name__ == '__main__':
do_timer_job(func, 1)

31
main.py Normal file
View File

@ -0,0 +1,31 @@
#!/usr/bin/python
# 项目主函数入口
import datetime
import time
from commons.logUtil import logger
from controllers.prController import app
from services.pullService import check_and_update_pull
from threading import Thread
import config.baseConfig as baseConfig
def monitoring_loop():
while True:
check_and_update_pull()
now = datetime.datetime.now()
logger.info('执行定时任务 :' + now.strftime('%Y-%m-%d %H:%M:%S'))
time.sleep(600)
def run():
monitoring_thread = Thread(target=monitoring_loop)
monitoring_thread.start()
app.run(host=baseConfig.host, port=baseConfig.port)
if __name__ == '__main__':
run()

Binary file not shown.

120
services/pullService.py Normal file
View File

@ -0,0 +1,120 @@
import sys
sys.path.append('..')
import config.baseConfig as baseConfig
import pymysql
import datetime
import commons.apiUtil as api
# 插入一条pull表记录
def insert_pull(values):
# 打开数据库连接
db = pymysql.connect(**baseConfig.db_config)
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()
# 插入语句
# value = (1, 2, "b", "b", now_time, now_time, 0)
ins_sql = """INSERT INTO pull(`pullid`, `index`, `owner`, `repo`, `created`, `checktime`, `status`)
VALUES (%s, %s, "%s", "%s","%s", "%s", %s)""" % values
print(ins_sql)
try:
cursor.execute(ins_sql)
db.commit()
except Exception as e:
print("ERR0R!insert pull error:")
db.rollback()
print(e)
# 关闭数据库连接
db.close()
# 更新一条pull表记录
def update_pull(index, owner, repo):
# 打开数据库连接
db = pymysql.connect(**baseConfig.db_config)
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()
# 更新语句
sql_update = "UPDATE pull set `status` = 1 where `index` = '%d' AND `owner` = '%s' AND `repo` = '%s'" % (
index, owner, repo)
try:
cursor.execute(sql_update)
db.commit()
except Exception as e:
print("ERR0R!update pull error:")
db.rollback()
print(e)
# 关闭数据库连接
db.close()
# 查询并更新
def check_and_update_pull():
# 打开数据库连接
db = pymysql.connect(**baseConfig.db_config)
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()
now_time = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S')
sql_query = "select * from pull where `status` = 0 and `checktime` < '%s'" % now_time
# print(sql_query)
try:
# 执行SQL语句
cursor.execute(sql_query)
# 获取所有记录列表
results = cursor.fetchall()
print(results)
for item in results:
# 调用接口查询pr状态
# (7, 1, 2, 'b', 'b', datetime.datetime(), None, datetime.datetime(2023, 3, 23, 11, 46, 13), 0)
# 提取 pr记录信息
pull_id = item[0]
pull_index = item[2]
owner = item[3]
repo = item[4]
pr_infor = api.get_pull_infor(owner, repo, pull_index)
print(pr_infor)
issue_id = pr_infor["issue"]["id"];
# pr 状态标志位0表示未完成1表示已经完成
flag = check_pr(pr_infor)
# 数据库 pr记录 更新语句
sql_update = ""
# 若新增评论或者状态为关闭 则修改数据库
if flag == 1:
# pr已完成更新语句
sql_update = "UPDATE pull set status = 1 where id = '%d'" % pull_id
# 否则对不活跃的pr调用接口发表评论提醒处理
try:
cursor.execute(sql_update)
db.commit()
except Exception as e:
db.rollback()
print("ERR0R!update pull error:")
print(e)
except Exception as e:
db.rollback()
print("ERR0R!deal pull error:")
print(e)
# 关闭数据库连接
db.close()
# 检查 pr状态已完成返回1未完成返回0
def check_pr(pr_infor):
# pr已经关闭
if pr_infor["status"] == "closed":
return 1
# pr有动态
if pr_infor["issue"]["journals_count"] != 0:
return 1
return 0
if __name__ == '__main__':
check_and_update_pull()