Skip to content

Commit a9acb1e

Browse files
committed
Add PagePro PR reminder
1 parent b892cde commit a9acb1e

File tree

4 files changed

+145
-18
lines changed

4 files changed

+145
-18
lines changed

scripts/github-pr-reminder-network.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ const getMessage = async robot => {
6868
}
6969
let response = "";
7070
if (soon.length > 0) {
71-
response += `**The following PRs will be over the threshold of ${threshold} days soon:**\n`
71+
response += `**<@&1329387007335727124> The following PRs will be over the threshold of ${threshold} days soon:**\n`
7272
soon.forEach(pr => {
7373
response += `**PR #${pr.number}:** ${pr.title} <${pr['html_url']}>\n`
7474
})
7575
}
7676
if (over.length > 0) {
77-
response += `**The following PRs are over the threshold of ${threshold} days:**\n`
77+
response += `**<@&1329387007335727124> The following PRs are over the threshold of ${threshold} days:**\n`
7878
over.forEach(pr => {
7979
response += `**PR #${pr.number}:** ${pr.title} <${pr['html_url']}>\n`
8080
})
@@ -99,6 +99,7 @@ module.exports = function(robot) {
9999

100100
robot.hear(/!networkReminder/, async res => {
101101
if (!isPrivateDiscordMessage(robot.client, res)) return
102+
console.log("Received !networkReminder command - please wait while I query the Github API");
102103
const message = await getMessage(robot);
103104
res.send(message);
104105
});
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
const { isPrivateDiscordMessage } = require('./utils/channels.js')
2+
const { getBusinessDatesCount } = require('./utils/dates.js')
3+
const { prHelpers } = require('./utils/prs.js')
4+
const CronJob = require('cron').CronJob
5+
require('dotenv').config()
6+
7+
const processPRs = async robot => {
8+
const roomId = process.env.HUBOT_DISCORD_DEV_CHANNEL
9+
const message = await getMessage(robot);
10+
if (message === "") {
11+
console.log("No PRs to remind about")
12+
return;
13+
}
14+
robot.messageRoom(roomId, message.trim());
15+
}
16+
17+
const getMessage = async robot => {
18+
19+
20+
const threshold = 3 // number of days since interaction
21+
const github = require('githubot')(robot)
22+
const repo = github.qualified_repo("colonyCDapp")
23+
const BASE_URL = `https://api.github.com/repos/${repo}`
24+
25+
26+
const { getPRs, getReviews, getComments, getPREvents, getPRCommits, getTeamMembers } = prHelpers(robot)
27+
const teamMembers = await getTeamMembers('joincolony', 'pagepro')
28+
const pageproLogins = teamMembers.map(m => m.login).filter(l => ["rdig","arrenv"].indexOf(l) === -1);
29+
const prs = await getPRs(BASE_URL)
30+
31+
const over = [];
32+
33+
for (const pr of prs) {
34+
// console.log(pr);
35+
if (pr.draft) {
36+
continue;
37+
}
38+
if (pr.labels.filter(label => label.name === 'on-hold').length > 0) {
39+
continue;
40+
}
41+
// This is only intended to alert about PRs that have been made by Pagepro team members
42+
if (!pageproLogins.includes(pr.user.login)) {
43+
continue;
44+
}
45+
const reviews = await getReviews(pr);
46+
const comments = await getComments(pr);
47+
const events = await getPREvents(pr);
48+
const commits = await getPRCommits(pr);
49+
50+
const labellingEvents = events.filter(event => event.event === 'labeled' || event.event === 'unlabeled');
51+
52+
const last_review_timestamp = reviews.length > 0 ? reviews[reviews.length - 1].submitted_at : pr.created_at;
53+
const last_comment_timestamp = comments.length > 0 ? comments[comments.length - 1].created_at : pr.created_at;
54+
const last_label_timestamp = labellingEvents.length > 0 ? labellingEvents[labellingEvents.length - 1].created_at : pr.created_at;
55+
const last_commit_author_timestamp = commits.length > 0 ? commits[commits.length - 1].commit.author.date : pr.created_at;
56+
57+
const last_event_timestamp = Math.max(new Date(last_review_timestamp), new Date(last_comment_timestamp), new Date(last_label_timestamp), new Date(last_commit_author_timestamp));
58+
59+
60+
const days = getBusinessDatesCount(new Date(last_event_timestamp), new Date());
61+
if (days >= threshold) {
62+
over.push(pr);
63+
}
64+
}
65+
let response = "";
66+
67+
if (over.length > 0) {
68+
response += `** <@&1293125237344571442> The following PagePro PRs haven't had any activity for the last ${threshold} days:**\n`
69+
over.forEach(pr => {
70+
response += `**PR #${pr.number}:** ${pr.title} <${pr['html_url']}>\n`
71+
})
72+
}
73+
return response;
74+
}
75+
76+
const setupCronJob = robot => {
77+
const job = new CronJob({
78+
// Every weekday 08:30h London
79+
cronTime: '00 30 08 * * 1-5',
80+
onTick: () => {
81+
processPRs(robot)
82+
},
83+
start: false,
84+
timeZone: 'Europe/London'
85+
})
86+
job.start()
87+
}
88+
module.exports = function(robot) {
89+
setupCronJob(robot)
90+
91+
robot.hear(/!pageproReminder/, async res => {
92+
if (!isPrivateDiscordMessage(robot.client, res)) return
93+
console.log("Received !pageproReminder command - please wait while I query the Github API");
94+
const message = await getMessage(robot);
95+
res.send(message);
96+
});
97+
}

scripts/utils/dates.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ const moment = require('moment-timezone');
33
const getBrain = require('./brain');
44
const getTimezoneFromMap = getBrain('timezones').getFromMap;
55

6-
76
const getOffsetDate = (offset, timestamp = Date.now()) => {
87
const d = new Date(timestamp + offset * 60 * 60 * 1000);
98
return `${d.getUTCFullYear()}-${d.getUTCMonth() + 1}-${d.getUTCDate()}`;
@@ -76,6 +75,20 @@ const parseNaturalDate = (expr, user, robot) => {
7675
return dateStart;
7776
};
7877

78+
function getBusinessDatesCount(startDate, endDate) {
79+
let count = 0;
80+
if (startDate > endDate) {
81+
return count;
82+
}
83+
const curDate = new Date(startDate.getTime());
84+
while (curDate.toDateString() !== endDate.toDateString()) {
85+
const dayOfWeek = curDate.getDay();
86+
if(dayOfWeek !== 0 && dayOfWeek !== 6) count++;
87+
curDate.setDate(curDate.getDate() + 1);
88+
}
89+
return count;
90+
}
91+
7992
module.exports = {
8093
getOffsetDate,
8194
getOffsetDay,
@@ -86,4 +99,5 @@ module.exports = {
8699
dateIsInRange,
87100
dateIsOlderThan,
88101
parseNaturalDate,
102+
getBusinessDatesCount,
89103
};

scripts/utils/prs.js

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,45 @@
11
const { findLast } = require('lodash')
22
require('dotenv').config()
33

4+
5+
46
const prHelpers = robot => {
57
const github = require('githubot')(robot)
68
const repo = github.qualified_repo(process.env.HUBOT_GITHUB_REPO)
79
const BASE_URL = `https://api.github.com/repos/${repo}`
810

11+
const ghget = async function(url) {
12+
return new Promise(resolve => {
13+
github.get(url, res => {
14+
resolve(res)
15+
})
16+
})
17+
}
18+
19+
920
const getPRs = async (base_url) => {
1021
if (!base_url) {
1122
base_url = BASE_URL
1223
}
24+
const prs = [];
25+
let n = 1;
26+
let pageContents = ["temp"];
27+
while (pageContents.length > 0) {
28+
pageContents = await ghget(`${base_url}/pulls?page=${n}`);
29+
prs.push(...pageContents);
30+
n++;
31+
}
32+
return prs;
33+
// return new Promise(resolve => {
34+
// github.get(`${base_url}/pulls`, res => {
35+
// resolve(res)
36+
// })
37+
// })
38+
}
39+
40+
const getTeamMembers = async (org_name, team_slug) => {
1341
return new Promise(resolve => {
14-
github.get(`${base_url}/pulls`, res => {
42+
github.get(`https://api.github.com/orgs/${org_name}/teams/${team_slug}/members`, res => {
1543
resolve(res)
1644
})
1745
})
@@ -58,13 +86,6 @@ const prHelpers = robot => {
5886
const events = []
5987
let nOnPage = 30
6088
let page = 1
61-
const ghget = async function(url) {
62-
return new Promise(resolve => {
63-
github.get(url, res => {
64-
resolve(res)
65-
})
66-
})
67-
}
6889

6990
while (nOnPage === 30) {
7091
const res = await ghget(`${pr.issue_url}/events?per_page=${nOnPage}&page=${page}`)
@@ -80,13 +101,6 @@ const prHelpers = robot => {
80101
const reviews = []
81102
let nOnPage = 30
82103
let page = 1
83-
const ghget = async function(url) {
84-
return new Promise(resolve => {
85-
github.get(url, res => {
86-
resolve(res)
87-
})
88-
})
89-
}
90104
while (nOnPage === 30) {
91105
const res = await ghget(`${pr.url}/reviews?per_page=${nOnPage}&page=${page}`)
92106
nOnPage = res.length
@@ -171,6 +185,7 @@ const prHelpers = robot => {
171185
getReviews,
172186
getComments,
173187
getPRCommits,
188+
getTeamMembers,
174189
}
175190
}
176191

0 commit comments

Comments
 (0)