diff --git a/README.md b/README.md index 1261ede..918d829 100644 --- a/README.md +++ b/README.md @@ -130,27 +130,28 @@ issue 被 assign 后,在 IM 群中提醒当事人。 ```javascript if(group['type']==='dingtalk'){ return dingtalk.sendMarkdown(group,content,atUid,uidArr); - }else{ - // 其他IM接入示例 - return otherim.sendMarkdown(group,content,atUid,uidArr); - } - ``` +}else{ + // 其他IM接入示例 + return otherim.sendMarkdown(group,content,atUid,uidArr); +} +``` - 配置:在config.json里面填写im的type为自己的im类型。 ```json - "good-first-issue-notifier": { - "enable": true, - "channels": [ - { - "type": "dingtalk", - "urls": [ - "https://oapi.dingtalk.com/robot/send?access_token=your_token" - ], - "title": "goodFirstIssue认领", - "atUid": [], - "atAll": true - } - ] - } + + "good-first-issue-notifier": { +"enable": true, +"channels": [ +{ +"type": "dingtalk", +"urls": [ +"https://oapi.dingtalk.com/robot/send?access_token=66113ea1b10247d25149158fb4b765da6c3c57bddca2682215122cc205e2ac70" +], +"title": "goodFirstIssue认领", +"atUid": [], +"atAll": true +} +] +} ``` ## 使用文档 diff --git a/src/const.js b/src/const.js index fffafae..8a9a2b1 100644 --- a/src/const.js +++ b/src/const.js @@ -1,7 +1,6 @@ //这个是固定写死的。 -const configNames = ['liveness-check','good-first-issue-notifier','mergeRepo','dangerousIssuesConfig','dingTalkGroupConfig'] - +const configNames = ['liveness-check','good-first-issue-notifier','issue-notifier','mergeRepo','dangerousIssuesConfig','dingTalkGroupConfig'] /** * 按照优先级获取配置 * @param repoConfig diff --git a/src/dao/dangerous_issue.js b/src/dao/dangerous_issue.js index 1f80382..4863a8f 100644 --- a/src/dao/dangerous_issue.js +++ b/src/dao/dangerous_issue.js @@ -1,8 +1,9 @@ const fs = require("fs"); +const moment = require("moment"); +const fetch = require("node-fetch"); const config = require("../../configs/config.json"); const {getConfig} = require("../const"); const { getConn, query } = require("./mysql_conn"); -const { getImDao } = require("./im"); const {utils} = require("../utils/time_utils"); const mysqlDao = { @@ -44,6 +45,7 @@ const mysqlDao = { const dingTalkDao = { issuesForDingTalk: new Map(), + livenessCheck: new Map(), start: function () { }, // put the issue into the memory list @@ -76,7 +78,7 @@ const dingTalkDao = { sendLiveness:async function(livenessCheckElement,owner,ownerDingTalkGroupConfig){ if (livenessCheckElement > 0) { let livenessContent = this.concatLivenessContent(owner, livenessCheckElement, ownerDingTalkGroupConfig); - await getImDao.send(livenessContent, null, false, true, "liveness", ownerDingTalkGroupConfig); + await this.send(livenessContent, null, false, true, "liveness", ownerDingTalkGroupConfig); } else { await this.sendSuccessLivecheck(owner, ownerDingTalkGroupConfig) } @@ -89,7 +91,7 @@ const dingTalkDao = { const ownerDingTalkGroupConfig = getConfig(null, config.orgRepoConfig[owner]['dingTalkGroupConfig'], config.generalConfig['dingTalkGroupConfig']); if (value.length > 0) { let issueContent = this.concatIssueContent(owner, value, ownerDingTalkGroupConfig); - await getImDao().send(issueContent, null, false, true, "issue", ownerDingTalkGroupConfig); + await this.send(issueContent, null, false, true, "issue", ownerDingTalkGroupConfig); } else { await this.sendNoIssue(owner, ownerDingTalkGroupConfig) } @@ -207,24 +209,24 @@ const dingTalkDao = { `注:liveness check会检查每个项目的健康情况,如果满足下列条件会被归类为“腐烂级”项目:\n` + `- 存在大于30天未回复的 issue \n` + `- 连续4周活跃度小于20\n`; - await getImDao().send(content, null, false, false, "liveness", dingTalkGroupConfig); + await this.send(content, null, false, false, "liveness", dingTalkGroupConfig); }, sendNoIssue: async function (owner, dingTalkGroupConfig) { let awards = [ { - content: `${owner}目前没有舆情 issue ,大家回复很及时,奖励一人一辆特斯拉!\n`, + content: `${owner}社区目前没有舆情 issue ,大家回复很及时,奖励一人一辆特斯拉!\n`, img: "https://gw.alipayobjects.com/mdn/rms_6ac329/afts/img/A*PXPwR6je8-MAAAAAAAAAAAAAARQnAQ", }, { - content: `${owner}目前没有舆情 issue ,大家回复很及时,奖励一人一辆 SpaceX 火箭!\n`, + content: `${owner}社区目前没有舆情 issue ,大家回复很及时,奖励一人一辆 SpaceX 火箭!\n`, img: "https://gw.alipayobjects.com/mdn/rms_6ac329/afts/img/A*J7MsQKCp-H8AAAAAAAAAAAAAARQnAQ", }, { - content: `${owner}目前没有舆情 issue ,大家回复很及时,奖励一人一个 脑机接口!\n`, + content: `${owner}社区目前没有舆情 issue ,大家回复很及时,奖励一人一个 脑机接口!\n`, img: "https://img-blog.csdnimg.cn/img_convert/15b784912f4511cbb9d35bb2c1bf5e91.png", }, { - content: `${owner}目前没有舆情 issue ,大家回复很及时,奖励一人一罐 可口可乐!\n`, + content: `${owner}社区目前没有舆情 issue ,大家回复很及时,奖励一人一罐 可口可乐!\n`, img: "https://gw.alipayobjects.com/mdn/rms_6ac329/afts/img/A*v6SsRKjmOWYAAAAAAAAAAAAAARQnAQ", }, ]; @@ -232,10 +234,178 @@ const dingTalkDao = { let idx = Math.floor(Math.random() * awards.length); let content = awards[idx].content; let img = awards[idx].img; - await getImDao().send(content, null, false, false, "issue", dingTalkGroupConfig); - await getImDao().sendImage(img, null, false, false, "issue", owner, dingTalkGroupConfig); - } + await this.send(content, null, false, false, "issue", dingTalkGroupConfig); + await this.sendImage(img, null, false, false, "issue", owner, dingTalkGroupConfig); + }, + /** + * 这里的owner等于owner好像 + * @param content + * @param atUid + * @param isAtAll + * @param isNegative + * @param topicType + * @param dingTalkGroupConfig + */ + send: async function (content, atUid, isAtAll, isNegative, topicType,dingTalkGroupConfig) { + const topicTypeLiveness = "liveness"; + const topicTypeIssue = "issue"; + const groups = dingTalkGroupConfig["groups"] + for (let group of groups) { + // 1.validate + // 检查url是否为空 + if (group['url'] == null || group['url'].length === 0) { + console.log("DingTalk url is empty"); + continue; + } + // check topic projects + // 检查group.topicProjects中是否包含project + // if (!this.interested(group['topicProjects'], project)) { + // continue; + // } + // check topicTypesIgnore + // 检查group['topicTypesIgnore']是否为*或者是否等于topicType + if (this.isIgnoredTopicType(group['topicTypesIgnore'], topicType)) { + continue; + } + // check topicTypesOnly + // 检查group['topicTypesOnly']是否为*或者是否等于topicType + if (!this.interestedTopicType(group['topicTypesOnly'], topicType)) { + continue; + } + // check owner in content + // 检查content中是否有group.owner,没有则替换this.owner + let newContent = content; + + // append text + // 添加链接文本 + if (topicType === topicTypeIssue) { + if (isNegative) { + newContent += this.nullToEmpty(group['issueWarningText']); + } else { + //没有issue,不会发消息,这个实际没有用到 + newContent += this.nullToEmpty(group['issueCongratulationText']); + } + } else if (topicType === topicTypeLiveness) { + if (isNegative) { + newContent += this.nullToEmpty(group['livenessWarningText']); + } else { + newContent += this.nullToEmpty(group['livenessCongratulationText']); + } + } + let uidArr = []; + if (atUid != null) { + uidArr = atUid; + } + // 2. send request + //发送请求 + return fetch(group.url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + at: { + atMobiles: [""], + atUserIds: uidArr, + isAtAll: isAtAll, + }, + text: { + content: newContent, + }, + msgtype: "text", + title: ``, + }), + }) + // 3. parse + .then((res) => { + return res.json(); + }) + .then((json) => { + console.log( `发送钉钉:${newContent}结果:`) + console.log(json) + }); + } + }, + nullToEmpty(str) { + // 如果str为null,则返回空字符串 + if (str == null) { + return ""; + } + // 否则返回str + return str; + }, + interested: function (topicProjects, project) { + // 如果topicProjects为空或者长度为0或者topicProjects为* + if ( + topicProjects == null || + topicProjects.length == 0 || + topicProjects == "*" + ) { + // 返回true + return true; + } + // 如果topicProjects中包含project + if (topicProjects.indexOf(project) >= 0) { + // 返回true + return true; + } + // 否则返回false + return false; + }, + sendImage: async function (imageUrl, atUid, isAtAll, isNegative, topicType,owner,option) { + const groups = option["groups"] + for (let group of groups) { + // 1.validate + if (group.url == null || group.url.length == 0) { + console.log("send image:DingTalk url is empty"); + continue; + } + // check topic project + // if (!this.interested(group['topicProjects'], project)) { + // continue; + // } + // check topicTypesIgnore + if (this.isIgnoredTopicType(group['topicTypesIgnore'], topicType)) { + continue; + } + // check topicTypesOnly + if (!this.interestedTopicType(group['topicTypesOnly'], topicType)) { + continue; + } + // check owner in content + let nickName = owner; + if (group['nickName'] != null) { + nickName = group['nickName']; + } + // 2. send request + return fetch(group.url, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + msgtype: "markdown", + markdown: { + title: nickName+"issue SOFAStack", + text: `![](${imageUrl}) \n`, + }, + at: { + atMobiles: [""], + atUserIds: [atUid], + isAtAll: isAtAll, + }, + }), + }) + // 3. parse + .then((res) => { + return res.json(); + }) + .then((json) => console.log("发送图片结果:",json)); + } + }, }; const fsDAOImpl = { @@ -322,8 +492,10 @@ module.exports = { fsDAOImpl.commit(); dingTalkDao.commit(); }, - + getDingTalkDao() { + return dingTalkDao; + }, getMysqlDao(){ return mysqlDao; } -}; \ No newline at end of file +}; diff --git a/src/dao/dingtalk.js b/src/dao/dingtalk.js index e2d6d0f..0df8207 100644 --- a/src/dao/dingtalk.js +++ b/src/dao/dingtalk.js @@ -1,64 +1,110 @@ const fetch = require("node-fetch"); -//钉钉发送client -const dingtalk = { - send : function send(group, newContent, uidArr, isAtAll) { - return fetch(group.url, { + +// https://open.dingtalk.com/document/group/custom-robot-access +const dingTalkDao = { + // 发送消息 + send: function (groupUrls, content, title, atUid, isAtAll) { + // 遍历groupUrls数组 + for (let group of groupUrls) { + // 1.validate + // 判断groupUrls数组是否为空 + if (group == null || group.length == 0) { + console.log("DingTalk groupUrls is empty"); + continue; + } + let uidArr = []; + if (atUid != null) { + uidArr = atUid; + } + // 2. send request + // 发送请求 + fetch(group, { method: "POST", headers: { - "Content-Type": "application/json", - Accept: "application/json", + "Content-Type": "application/json", + Accept: "application/json", }, body: JSON.stringify({ - at: { - atMobiles: [""], - atUserIds: uidArr, - isAtAll: isAtAll, - }, - text: { - content: newContent, - }, - msgtype: "text" + at: { + atMobiles: [""], + atUserIds: uidArr, + isAtAll: isAtAll, + }, + text: { + content: content, + }, + msgtype: "text", + title: `${title}`, }), - }) + }) + // 3. parse .then((res) => { - return res.json(); + return res.json(); }) - .then((json) => { - console.log( `发送IM:${newContent}结果:`); - console.log(json); - }); -}, + .then((json) => console.log(json)); + } + }, - sendMarkdown :function sendMarkdown(group, nickName, imageUrl, atUid, isAtAll) { - // 2. send request - fetch(group.url, { + // 发送markdown消息 + sendMarkdown: function (groupUrls, content, title, atUid, isAtAll) { + // 遍历groupUrls数组 + for (let group of groupUrls) { + // 1.validate + // 判断groupUrls数组是否为空 + if (group == null || group.length == 0) { + console.log("DingTalk groupUrls is empty"); + continue; + } + let uidArr = []; + if (atUid != null) { + uidArr = atUid; + } + // 2. send request + // 发送请求 + fetch(group, { method: "POST", headers: { - "Content-Type": "application/json", - Accept: "application/json", + "Content-Type": "application/json", + Accept: "application/json", }, body: JSON.stringify({ - msgtype: "markdown", - markdown: { - title: nickName+" issue", - text: `![](${imageUrl}) \n`, - }, - at: { - atMobiles: [""], - atUserIds: [atUid], - isAtAll: isAtAll, - }, + msgtype: "markdown", + markdown: { + title: `${title}`, + text: content, + }, + at: { + atMobiles: [""], + atUserIds: uidArr, + isAtAll: isAtAll, + }, }), - }) + }) // 3. parse .then((res) => { - return res.json(); + return res.json(); }) - .then((json) => console.log("发送图片结果:", json)); -} + .then((json) => console.log(json)); + } + }, + // 将null转换为空字符串 + nullToEmpty(str) { + if (str == null) { + return ""; + } + return str; + }, }; - module.exports = { - dingtalk:dingtalk + // 发送消息 + send: function (groupUrls, content, title, atUid, isAtAll) { + // 调用dingTalkDao的send方法,发送消息 + return dingTalkDao.send(groupUrls, content, title, atUid, isAtAll); + }, + // 发送markdown消息 + sendMarkdown: function (groupUrls, content, title, atUid, isAtAll) { + // 调用dingTalkDao的sendMarkdown方法,发送markdown消息 + return dingTalkDao.sendMarkdown(groupUrls, content, title, atUid, isAtAll); + }, }; \ No newline at end of file diff --git a/src/dispatcher.js b/src/dispatcher.js index a2475fe..e6fef13 100644 --- a/src/dispatcher.js +++ b/src/dispatcher.js @@ -3,8 +3,6 @@ const { monthly } = require("./service/monthly"); const { issueScanner } = require("./service/issue_scanner"); const { setConfig, getConn} = require("./dao/mysql_conn"); -const configNames = ['liveness-check','good-first-issue-notifier','mergeRepo','dangerousIssuesConfig','dingTalkGroupConfig'] - // 定义一个函数dispatch,用于调度 function dispatch(config,since,to) { @@ -33,4 +31,4 @@ function dispatch(config,since,to) module.exports = { dispatch: dispatch, -}; \ No newline at end of file +}; diff --git a/src/metrics/issues.js b/src/metrics/issues.js index 19f81fa..ead21ab 100644 --- a/src/metrics/issues.js +++ b/src/metrics/issues.js @@ -72,21 +72,21 @@ async function listOpenIssues(token, owner, repo) { }, }), }) - .then((res) => { - // 返回响应的json数据 - return res.json(); - }) - .then((d) => { - // 返回仓库的open issues列表 - return d.data.repository.issues; - }); + .then((res) => { + // 返回响应的json数据 + return res.json(); + }) + .then((d) => { + // 返回仓库的open issues列表 + return d.data.repository.issues; + }); } // async函数,用于调用GitHub API,获取好的第一个问题 async function listGoodFirstIssues(token, owner, repo, createdSince) { // 打印出调用GitHub API的信息 console.log( - "Invoking github API to fetch good first issues. owner: " + + "Invoking github API to fetch good first issues. owner: " + owner + " repo:" + repo + @@ -155,14 +155,14 @@ async function listGoodFirstIssues(token, owner, repo, createdSince) { }, }), }) - .then((res) => { - return res.json(); - }) - .then((d) => { - return d.data.repository.issues; - }) - .then((issues) => filterIssuesByCreatedSince(issues, createdSince)) - .then((issues) => groupByDifficulty(issues)); + .then((res) => { + return res.json(); + }) + .then((d) => { + return d.data.repository.issues; + }) + .then((issues) => filterIssuesByCreatedSince(issues, createdSince)) + .then((issues) => groupByDifficulty(issues)); } // 遍历issues数组,根据标签名将问题分别放入不同的数组中 @@ -176,7 +176,7 @@ function groupByDifficulty(issues) { issues.forEach((issue) => { for (const idx in issue.labels) { const labelName = issue.labels[idx]; - + // 遍历每一个问题,根据标签名将其放入不同的数组中 if (labelName == "easy") { result["easy"].push(issue); @@ -237,8 +237,8 @@ function filterIssuesByCreatedSince(issues, createdSince) { async function listDangerousOpenIssues(token, owner, repo, to) { // 调用listOpenIssues函数,获取所有 return listOpenIssues(token, owner, repo).then((issues) => - // 调用filterOutDangerousIssues函数,过滤出危险 - filterOutDangerousIssues(issues, to,owner,repo) + // 调用filterOutDangerousIssues函数,过滤出危险 + filterOutDangerousIssues(issues, to,owner,repo) ); } @@ -261,7 +261,7 @@ function filterOutDangerousIssues(issues, to,owner,repo) { return; } - // 判断是否有成员回复了该信息,否则直接返回 + // 判断是否有成员回复了该信息,否则直接返回 if (someMemberHasReplied_graphql(issue)) { return; } @@ -276,6 +276,11 @@ function filterOutDangerousIssues(issues, to,owner,repo) { return; } + //判断是否有协作者评论,如果有直接返回 + if(hasCollaboratorComment(issue)){ + return; + } + // 判断issue是否早30天创建,如果是则直接返回 // check baseline let createDay = moment(issue.createdAt, "YYYY-MM-DDTHH:mm:ssZ"); @@ -318,10 +323,10 @@ function withSpecialLabels(issue) { // 遍历issue的labels.edges,检查是否有"help wanted","good first issue","WIP","wip"中的一个 issue.labels.edges.forEach((labelNode) => { if ( - labelNode.node.name == "help wanted" || - labelNode.node.name == "good first issue" || - labelNode.node.name == "WIP" || - labelNode.node.name == "wip" + labelNode.node.name == "help wanted" || + labelNode.node.name == "good first issue" || + labelNode.node.name == "WIP" || + labelNode.node.name == "wip" ) { found = true; } @@ -333,7 +338,7 @@ function withSpecialLabels(issue) { function isCommunityIssue_graphql(issue) { // 判断issue的authorAssociation是否不是MEMBER和OWNER return ( - issue.authorAssociation != "MEMBER" && issue.authorAssociation != "OWNER" && issue.authorAssociation != "COLLABORATOR" + issue.authorAssociation != "MEMBER" && issue.authorAssociation != "OWNER" && issue.authorAssociation != "COLLABORATOR" ); } @@ -341,14 +346,14 @@ function someMemberHasReplied_graphql(issue) { // 遍历issue的comments.nodes for (let comment of issue.comments.nodes) { // 如果comment的作者和issue的作者相同,则跳过 - if (comment.author.login == issue.author.login) { + if (issue.author!=null && comment.author !=null && comment.author.login == issue.author.login) { continue; } // 如果comment的作者和issue的作者关系是MEMBER、OWNER、CONTRIBUTOR,则返回true if ( - comment.authorAssociation == "MEMBER" || - comment.authorAssociation == "OWNER" || - comment.authorAssociation == "CONTRIBUTOR" + comment.authorAssociation == "MEMBER" || + comment.authorAssociation == "OWNER" || + comment.authorAssociation == "CONTRIBUTOR" ) { return true; } @@ -357,6 +362,24 @@ function someMemberHasReplied_graphql(issue) { return false; } +function hasCollaboratorComment(issue) { + // 遍历issue的comments.nodes + for (let comment of issue.comments.nodes) { + // 如果comment的作者和issue的作者相同,则跳过 + if (issue.author!=null && comment.author !=null && comment.author.login == issue.author.login) { + continue; + } + // 如果comment的作者和issue的作者关系是COLLABORATOR,则返回true + if ( + comment.authorAssociation == "COLLABORATOR" + ) { + return true; + } + } + // 否则返回false + return false; + +} module.exports = { // 列出OpenIssues listOpenIssues: listOpenIssues, @@ -378,4 +401,4 @@ module.exports = { mustReplyInXDays = days; } }, -}; \ No newline at end of file +}; diff --git a/src/notify.js b/src/notify.js index 19e743e..c0037ec 100644 --- a/src/notify.js +++ b/src/notify.js @@ -1,6 +1,6 @@ -const sender = require("./dao/im"); +const sender = require("./dao/dingtalk"); -sender.getImDao().sendMarkdown( +sender.sendMarkdown( [ // The webhook url of your dingtalk bot. For example: "https://oapi.dingtalk.com/robot/send?access_token=111111", diff --git a/src/service/issue_scanner.js b/src/service/issue_scanner.js index 6c63762..0b66804 100644 --- a/src/service/issue_scanner.js +++ b/src/service/issue_scanner.js @@ -4,7 +4,7 @@ const { } = require("../metrics/issues"); const dangerousIssueDAO = require("../dao/dangerous_issue"); const { weeklyScoreDAO } = require("../dao/weekly_score"); -const sender = require("../dao/im"); +const sender = require("../dao/dingtalk"); const { configNames,getConfig } = require("../const.js"); /** @@ -62,7 +62,27 @@ const issueScanner = { // 扫描仓库中的 good first issue const res = listGoodFirstIssues(config.generalConfig.graphToken, owner, repo, since) .then(async function (issues) { - + // The data structure looks like: + // { + // easy: [], + // medium: [ + // { + // project: 'layotto', + // title: 'Develop a new component for sms API; 为"短信 API" 开发新的组件', + // url: 'https://github.com/mosn/layotto/issues/830', + // labels: [Array] + // } + // ], + // hard: [ + // { + // project: 'layotto', + // title: 'generate a cli tool for Layotto; 生成 Layotto 命令行工具', + // url: 'https://github.com/mosn/layotto/issues/826', + // labels: [Array] + // } + // ], + // unknown: [] + // } let text = `${owner}社区${repo}项目新增了几个 good first issue, 欢迎感兴趣的朋友认领! \n\r`; @@ -217,3 +237,4 @@ function appendGoodFirstIssues(text, issues) { module.exports = { issueScanner: issueScanner, }; + diff --git a/src/service/weekly.js b/src/service/weekly.js index d735cf4..2b446cb 100644 --- a/src/service/weekly.js +++ b/src/service/weekly.js @@ -62,28 +62,6 @@ let octokit = null; if (mergeRepoConfig[key] == null) { repo2project.set(key, result); } - // 过滤出有危险问题的列表 ,修改相关bug,这里不在重复发送钉钉 - // let dangerousIssues = filterOutDangerousIssues(result.openIssues, to, result.owner, result.repo); - // const dangerIssueFilterd = dangerousIssues.filter(res => res.duration < config.generalConfig.dangerousIssuesConfig.mustReplyInXDays); - // if (dangerIssueFilterd.length === 0) { - // dangerousIssueDAO.insert(null, null, null, null, owner) - // } else { - // dangerIssueFilterd.forEach((result) => { - // dangerousIssueDAO.insert(result.duration, result.project, result.title, result.url, result.keyword); - // }); - // } - // 埋点数据 ToDo - // dangerousIssueDAO.getMysqlDao().sendAlarmMysql({ - // scanFrom:since, - // scanTo:to, - // owner:owner, - // repo:repo, - // issueNum:dangerIssueFilterd.length, - // alarmContent:"", - // alarmStatus:dangerIssueFilterd.length===0?'success':"fail", - // alarmType:'issue', - // alarmChannel:'dingding' - // }) resultArr.push(result); }); @@ -94,12 +72,11 @@ let octokit = null; } //owner循环结束 Promise.all(resultPromiseArr).then(async result => { - await mergeRepoToOtherRepo(resultArr, mergeRepoConfig, repo2project); - //插入数据库;发送钉钉 + await mergeRepoToOtherRepo(resultArr, mergeRepoConfig, repo2project); + //插入数据库;发送钉钉 insertDb(repo2project, since, to, config); // 提交weeklyScoreDAO weeklyScoreDAO.commit(); - // dangerousIssueDAO.commit(); }) @@ -116,14 +93,17 @@ let octokit = null; for (let i = 0; i < arr.length; i++) { let project = arr[i]; let key = project.owner + "/" + project.repo; - if (mergeRepo[key] == null) { + let mergeTarget = mergeRepo[key]; + if (mergeTarget == null) { continue; } - console.log(key + " should be merged"); - moveIssuesToOtherRepo(project.closeIssue, mergeRepo[key], repo2project, (project) => project.closeIssue); + console.log(`${key}仓库merge前的计算项:closeIssue:${repo2project.get(mergeTarget).closeIssue.size},closePr:${repo2project.get(mergeTarget).closePr.size},newIssue:${repo2project.get(mergeTarget).newIssue.size},newPr:${repo2project.get(mergeTarget).newPr.size}`) + moveIssuesToOtherRepo(project.closeIssue, mergeRepo[key], repo2project, (project) => project.closeIssue); moveIssuesToOtherRepo(project.closePr, mergeRepo[key], repo2project, (project) => project.closePr); moveIssuesToOtherRepo(project.newPr, mergeRepo[key], repo2project, (project) => project.newPr); moveIssuesToOtherRepo(project.newIssue, mergeRepo[key], repo2project, (project) => project.newIssue); + console.log(`${key}仓库merge后的计算项:closeIssue:${repo2project.get(mergeTarget).closeIssue.size},closePr:${repo2project.get(mergeTarget).closePr.size},newIssue:${repo2project.get(mergeTarget).newIssue.size},newPr:${repo2project.get(mergeTarget).newPr.size}`) + } } @@ -246,7 +226,7 @@ function getNickName(repo, repoOption) { return; } // 打印出issue的html_url和targetKey - console.log(issue.html_url + " should be merged into " + targetKey); + // console.log(issue.html_url + " should be merged into " + targetKey); // 如果targetKey为null或者repo2project.get(targetKey)为null,则跳过 if (targetKey == null || repo2project.get(targetKey) == null) { continue; @@ -273,44 +253,19 @@ function calculateScore(result) { 2 * result.new_forks + // 计算新贡献者数量 5 * result.new_contributors; + // console.log(result.repoName+"计算过程:2* newIssue("+result.newIssue.size+");3*newPr("+result.newPr.size+");4*prComment("+result.prComment.size+");2*closePr("+ + // result.closePr.size+");1*newStars("+result.new_stars+");2*newFork("+ result.new_forks+");5*newContributors("+result.new_contributors+")") return result; } -// 计算v2版本的分数 -function calculateScore_v2_add(result) { - // 计算分数 - result = calculateScore(result); - - // 设置截止日期 - const deadline = 30; - // 设置系数 - const k = 10; - // 遍历关闭的issue - result.closeIssue.forEach((e) => { - // 判断是否是社区问题且已经有人回复 - let care = isCommunityIssue(e) && someMemberHasReplied(e); - // 如果没有,则直接返回 - if (!care) { - return; - } - // 计算关闭到创建的时间差 - let duration = moment(e.closed_at, "YYYY-MM-DDTHH:mm:ssZ").diff( - moment(e.created_at, "YYYY-MM-DDTHH:mm:ssZ"), - "day" - ); - // 如果小于截止日期,则将系数加入分数 - if (duration < deadline) { - result.score += (k * (deadline - duration)) / deadline; - } - }); - // 返回结果 - return result; -} // 计算分数函数,用于计算分数 function calculateScore_v2_sub(config,result, to) { // 计算分数 + // console.log(result.repoName+"计算内容:"+JSON.stringify(result)) result = calculateScore(result); + let prevScore = result.score; + let dangerousIssueUrlList = new Array(); // 计算分数的系数 const k1 = 5; @@ -325,7 +280,9 @@ function calculateScore_v2_sub(config,result, to) { } else { result.score -= k1; } + dangerousIssueUrlList.push(issue.url); }); + console.log(result.repoName+"最初分数:"+prevScore+";危险issue数量(每个减去5或者7分):"+dangerousIssues.length+"个;综合得分:"+result.score+";危险ISSUE列表:"+JSON.stringify(dangerousIssueUrlList)); // 返回计算后的分数 return result; @@ -338,16 +295,7 @@ function isCommunityIssue(issue) { ); } -// 判断issue是否有回复 -function someMemberHasReplied(issue) { - // 如果issue没有评论,则返回false - if (issue.comments == 0) { - return false; - } - // let url = issue.timeline_url.replace("https: //", "https://"); - // https://api.github.com/repos/mosn/layotto/issues/214/timeline - return true; -} + // 函数collectIssueData用于收集GitHub上指定仓库的Issue和PR数据 function collectIssueData(orgName, repoName, since) { @@ -467,3 +415,4 @@ function isPr(d) { module.exports = { generateScoreReport: generateScoreReport, }; +