Skip to content

Commit c7167a3

Browse files
feat: slack and discord support added as notifiers (shubhaamgupta11#2)
* feat: refactored new issue logic * feat: refactored new pr logic * slack and discord added
1 parent 2518f1c commit c7167a3

File tree

6 files changed

+330
-234
lines changed

6 files changed

+330
-234
lines changed

action/index.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,43 @@ async function run() {
77
try {
88
const task = core.getInput("task");
99
const gitToken = core.getInput("git_token");
10-
const slackToken = core.getInput("slack_bot_token");
11-
const slackChannel = core.getInput("slack_channel");
10+
const notifier = core.getInput("notifier"); // 'slack' or 'discord'
11+
1212
const owner = core.getInput("repo_owner");
1313
const repo = core.getInput("repo_name");
14+
15+
// Slack-specific inputs
16+
const slackToken = core.getInput("slack_bot_token");
17+
const slackChannel = core.getInput("slack_channel");
1418
const slackIDType = core.getInput("slack_id_type");
1519
const slackID = core.getInput("slack_id");
1620

21+
// Discord-specific inputs
22+
const discordWebhookUrl = core.getInput("discord_webhook_url");
23+
1724
switch (task) {
1825
case "monitor-issues":
19-
await monitorIssues(gitToken, slackToken, slackChannel, owner, repo, slackIDType, slackID);
26+
await monitorIssues({
27+
gitToken,
28+
owner,
29+
repo,
30+
notifier,
31+
slackConfig: { slackToken, slackChannel, slackIDType, slackID },
32+
discordConfig: { discordWebhookUrl },
33+
});
2034
break;
35+
2136
case "monitor-prs":
22-
await monitorPRs(gitToken, slackToken, slackChannel, owner, repo, slackIDType, slackID);
37+
await monitorPRs({
38+
gitToken,
39+
owner,
40+
repo,
41+
notifier,
42+
slackConfig: { slackToken, slackChannel, slackIDType, slackID },
43+
discordConfig: { discordWebhookUrl },
44+
});
2345
break;
46+
2447
default:
2548
core.setFailed(`Unsupported task: ${task}`);
2649
}

action/integrations/discord.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const axios = require("axios");
2+
const delay = require("../utility/delay");
3+
const discordWrapper = (webhookUrl) => {
4+
const sendMessage = async (content) => {
5+
try {
6+
await axios.post(webhookUrl, { content });
7+
} catch (e) {
8+
console.error("Unable to send Discord message:", e.message);
9+
}
10+
};
11+
12+
return {
13+
sendMessage,
14+
};
15+
};
16+
17+
// Send Discord Notifications
18+
const sendDiscordNotification = async (webhookUrl, issues, repo) => {
19+
if (!issues.length) {
20+
console.log("No issues found within the specified time frame.");
21+
return;
22+
}
23+
24+
const discord = discordWrapper(webhookUrl);
25+
26+
for (const issue of issues) {
27+
const message = `
28+
**New Issue in ${repo}**
29+
**Title:** ${issue.title}
30+
**Labels:** ${issue.labels.map((label) => `\`${label}\``).join(", ")}
31+
**Link:** ${issue.url}
32+
`;
33+
34+
try {
35+
await discord.sendMessage(message);
36+
console.log(`Posted issue "${issue.title}" to Discord.`);
37+
} catch (error) {
38+
console.error(`Failed to post issue "${issue.title}" to Discord:`, error.message);
39+
}
40+
41+
console.log("Waiting for 30 seconds before sending the next message...");
42+
await delay(5 * 1000);
43+
}
44+
45+
console.log(`*** All issues posted on Discord for ${repo} ***`);
46+
};
47+
48+
module.exports = sendDiscordNotification;

action/integrations/slack.js

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const { WebClient } = require("@slack/web-api");
2+
const delay = require("../utility/delay");
3+
4+
const slackWrapper = (token, channel) => {
5+
const client = new WebClient(token);
6+
7+
const sendMessage = async (args) => {
8+
try {
9+
await client.chat.postMessage({
10+
...args,
11+
channel,
12+
unfurl_links: false,
13+
unfurl_media: false,
14+
});
15+
} catch (e) {
16+
console.error("Unable to send Slack message:", e.message);
17+
}
18+
};
19+
20+
return {
21+
sendMessage,
22+
client,
23+
channel,
24+
};
25+
};
26+
27+
const sendSlackNotification = async (slackToken, slackChannel, slackIDType, slackID, issues, repo) => {
28+
if (!issues.length) {
29+
console.log("No issues found within the specified time frame.");
30+
return;
31+
}
32+
33+
const slack = slackWrapper(slackToken, slackChannel);
34+
35+
const assigneeText =
36+
slackIDType === "group"
37+
? `*Assignee:* <!subteam^${slackID}> *(Mark as ACK or Done after triaging)*`
38+
: slackIDType === "user"
39+
? `*Assignee:* <@${slackID}> *(Mark as ACK or Done after triaging)*`
40+
: "";
41+
42+
for (const issue of issues) {
43+
const message = `
44+
:chart_with_upwards_trend: *New Issue in ${repo}*
45+
*-* *Title:* ${issue.title}
46+
*-* *Labels:* ${issue.labels.map((label) => `\`${label}\``).join(", ")}
47+
*-* *Link:* <${issue.url}|View Issue>
48+
${assigneeText}
49+
`;
50+
51+
try {
52+
await slack.sendMessage({ text: message });
53+
console.log(`Posted issue "${issue.title}" to Slack.`);
54+
} catch (error) {
55+
console.error(`Failed to post issue "${issue.title}" to Slack:`, error.message);
56+
}
57+
58+
console.log("Waiting for 30 seconds before sending the next message...");
59+
await delay(5 * 1000);
60+
}
61+
62+
console.log(`*** All issues posted on Slack for ${repo} ***`);
63+
};
64+
65+
module.exports = sendSlackNotification;

action/monitor-new-issue.js

Lines changed: 90 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,137 +1,112 @@
11
const axios = require("axios");
2-
const { WebClient } = require("@slack/web-api");
3-
4-
const slackWrapper = (token, channel) => {
5-
const client = new WebClient(token);
6-
7-
const sendMessage = async (args) => {
8-
try {
9-
await client.chat.postMessage({
10-
...args,
11-
channel,
12-
unfurl_links: false,
13-
unfurl_media: false,
14-
});
15-
} catch (e) {
16-
console.error("Unable to send Slack message:", e.message);
17-
}
18-
};
19-
20-
return {
21-
sendMessage,
22-
client,
23-
channel,
24-
};
25-
};
26-
27-
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2+
const sendSlackNotification = require("./integrations/slack");
3+
const sendDiscordNotification = require("./integrations/discord");
284

295
/**
306
* Fetch new issues created within a configurable timeframe.
31-
*
7+
*
328
* @param {string} gitToken - GitHub API token for authentication.
339
* @param {string} owner - Repository owner.
3410
* @param {string} repo - Repository name.
3511
* @param {number} hoursAgo - Timeframe in hours to fetch issues created since then.
3612
* @returns {Promise<Array>} - List of new issues created within the specified timeframe.
3713
*/
3814
const fetchNewIssues = async (gitToken, owner, repo, hoursAgo) => {
39-
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/issues`;
40-
const sinceDate = new Date(new Date().getTime() - hoursAgo * 60 * 60 * 1000).toISOString();
41-
42-
let newIssues = [];
43-
let page = 1;
44-
45-
try {
46-
while (true) {
47-
const response = await axios.get(apiUrl, {
48-
headers: { Authorization: `token ${gitToken}` },
49-
params: {
50-
state: "open", // Only fetch open issues
51-
sort: "created", // Sort by creation date
52-
direction: "desc", // Fetch the most recent issues first
53-
since: sinceDate, // Fetch issues created or updated after this timestamp
54-
per_page: 100, // Maximum results per page
55-
page, // Current page of the results
56-
},
57-
});
58-
59-
const issues = response.data.filter((issue) => {
60-
// Exclude pull requests and ensure the issue is newly created
61-
const createdAt = new Date(issue.created_at);
62-
return !issue.pull_request && createdAt >= new Date(sinceDate);
63-
});
64-
65-
// Map issues to include required fields
66-
newIssues.push(
67-
...issues.map((issue) => ({
68-
title: issue.title,
69-
url: issue.html_url,
70-
createdAt: issue.created_at,
71-
labels: issue.labels.map((label) => label.name),
72-
comments: issue.comments,
73-
}))
74-
);
75-
76-
// Exit loop if no more pages to fetch
77-
if (!response.headers["link"]?.includes('rel="next"')) break;
78-
79-
page++; // Move to the next page
80-
}
81-
82-
return newIssues;
83-
} catch (error) {
84-
console.error("Error fetching issues:", error.message);
85-
return [];
86-
}
87-
};
88-
89-
90-
const sendSlackNotification = async (slackToken, slackChannel, slackIDType, slackID, issues, repo) => {
91-
if (!issues.length) {
92-
console.log("No issues found within the specified time frame.");
93-
return;
94-
}
95-
96-
const slack = slackWrapper(slackToken, slackChannel);
97-
98-
const assigneeText =
99-
slackIDType === "group"
100-
? `*Assignee:* <!subteam^${slackID}> *(Mark as ACK or Done after triaging)*`
101-
: slackIDType === "user"
102-
? `*Assignee:* <@${slackID}> *(Mark as ACK or Done after triaging)*`
103-
: "";
15+
const apiUrl = `https://api.github.com/repos/${owner}/${repo}/issues`;
16+
const sinceDate = new Date(
17+
new Date().getTime() - hoursAgo * 60 * 60 * 1000
18+
).toISOString();
19+
20+
let newIssues = [];
21+
let page = 1;
22+
23+
try {
24+
while (true) {
25+
const response = await axios.get(apiUrl, {
26+
headers: { Authorization: `token ${gitToken}` },
27+
params: {
28+
state: "open", // Only fetch open issues
29+
sort: "created", // Sort by creation date
30+
direction: "desc", // Fetch the most recent issues first
31+
since: sinceDate, // Fetch issues created or updated after this timestamp
32+
per_page: 100, // Maximum results per page
33+
page, // Current page of the results
34+
},
35+
});
10436

105-
for (const issue of issues) {
106-
const message = `
107-
:chart_with_upwards_trend: *New Issue in ${repo}*
108-
*-* *Title:* ${issue.title}
109-
*-* *Labels:* ${issue.labels.map((label) => `\`${label}\``).join(", ")}
110-
*-* *Link:* <${issue.url}|View Issue>
111-
${assigneeText}
112-
`;
37+
const issues = response.data.filter((issue) => {
38+
// Exclude pull requests and ensure the issue is newly created
39+
const createdAt = new Date(issue.created_at);
40+
return !issue.pull_request && createdAt >= new Date(sinceDate);
41+
});
11342

114-
try {
115-
await slack.sendMessage({ text: message });
116-
console.log(`Posted issue "${issue.title}" to Slack.`);
117-
} catch (error) {
118-
console.error(`Failed to post issue "${issue.title}" to Slack:`, error.message);
43+
// Map issues to include required fields
44+
newIssues.push(
45+
...issues.map((issue) => ({
46+
title: issue.title,
47+
url: issue.html_url,
48+
createdAt: issue.created_at,
49+
labels: issue.labels.map((label) => label.name),
50+
comments: issue.comments,
51+
}))
52+
);
53+
54+
// Exit loop if no more pages to fetch
55+
if (!response.headers["link"]?.includes('rel="next"')) break;
56+
57+
page++; // Move to the next page
11958
}
12059

121-
console.log("Waiting for 30 seconds before sending the next message...");
122-
await delay(30 * 1000);
60+
return newIssues;
61+
} catch (error) {
62+
console.error("Error fetching issues:", error.message);
63+
return [];
12364
}
124-
125-
console.log(`*** All issues posted on Slack for ${repo} ***`);
12665
};
12766

128-
async function monitorIssues(gitToken, slackToken, slackChannel, owner, repo, slackIDType, slackID) {
129-
const hoursAgo = 6;
67+
async function monitorIssues({
68+
gitToken,
69+
owner,
70+
repo,
71+
notifier,
72+
slackConfig,
73+
discordConfig,
74+
}) {
75+
const hoursAgo = 24;
13076

13177
const issues = await fetchNewIssues(gitToken, owner, repo, hoursAgo);
132-
console.log("Issues to be notified via Slack:", issues.map((issue) => issue.title));
13378

134-
await sendSlackNotification(slackToken, slackChannel, slackIDType, slackID, issues, repo);
79+
if (notifier === "slack") {
80+
const {
81+
slackToken,
82+
slackChannel,
83+
slackIDType,
84+
slackID,
85+
} = slackConfig;
86+
console.log(
87+
"Sending notifications to Slack for issues:",
88+
issues.map((issue) => issue.title)
89+
);
90+
await sendSlackNotification(
91+
slackToken,
92+
slackChannel,
93+
slackIDType,
94+
slackID,
95+
issues,
96+
repo
97+
);
98+
} else if (notifier === "discord") {
99+
const {
100+
discordWebhookUrl,
101+
} = discordConfig;
102+
console.log(
103+
"Sending notifications to Discord for issues:",
104+
issues.map((issue) => issue.title)
105+
);
106+
await sendDiscordNotification(discordWebhookUrl, issues, repo);
107+
} else {
108+
throw new Error("Unsupported notifier. Use 'slack' or 'discord'.");
135109
}
136-
137-
module.exports = { monitorIssues };
110+
}
111+
112+
module.exports = monitorIssues;

0 commit comments

Comments
 (0)