-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.js
More file actions
159 lines (134 loc) · 4.78 KB
/
index.js
File metadata and controls
159 lines (134 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
const { Client, GatewayIntentBits } = require("discord.js");
const express = require("express");
const crypto = require("crypto");
require("dotenv").config();
const app = express();
const FORUM_CHANNEL_ID = process.env.FORUM_CHANNEL_ID;
const BOT_TOKEN = process.env.DISCORD_BOT_TOKEN;
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET;
const discord = new Client({
intents: [GatewayIntentBits.Guilds],
});
discord.once("ready", () => {
console.log(`Bot logged in as ${discord.user.tag}`);
});
// Middleware to capture raw body
app.use("/webhook", express.raw({ type: "application/json" }));
function verifyWebhookSignature(payload, signature) {
const expectedSignature =
"sha256=" +
crypto.createHmac("sha256", WEBHOOK_SECRET).update(payload).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expectedSignature)
);
}
async function findThreadByIssueNumber(issueNumber) {
const forum = await discord.channels.fetch(FORUM_CHANNEL_ID);
const threads = await forum.threads.fetchActive();
const threadName = `#${issueNumber} -`;
const thread = threads.threads.find((t) => t.name.startsWith(threadName));
if (!thread) {
const archivedThreads = await forum.threads.fetchArchived();
return archivedThreads.threads.find((t) => t.name.startsWith(threadName));
}
return thread;
}
async function getStarterMessage(thread) {
const messages = await thread.messages.fetch({ limit: 1 });
return messages.first();
}
app.post("/webhook", async (req, res) => {
try {
// Verify webhook signature
const signature = req.headers["x-hub-signature-256"];
if (!signature || !verifyWebhookSignature(req.body, signature)) {
console.log("Invalid webhook signature");
return res.sendStatus(401);
}
// Parse the verified payload
const payload = JSON.parse(req.body.toString());
const { action, issue, pull_request, comment, sender } = payload;
// Handle new issues (not PRs)
if (action === "opened" && issue && !pull_request) {
const forum = await discord.channels.fetch(FORUM_CHANNEL_ID);
await forum.threads.create({
name: `#${issue.number} - ${issue.title}`,
message: {
content: `**New Issue by ${sender.login}:** <${issue.html_url}>\n\n${
issue.body || "No description"
}`,
},
});
console.log(`Created thread for issue #${issue.number}`);
}
// Handle new PRs
else if (action === "opened" && pull_request) {
// Check if thread already exists (from an issue)
let thread = await findThreadByIssueNumber(pull_request.number);
if (thread) {
// Issue already exists, just notify about the PR
await thread.send(
`**${sender.login} opened a pull request:** <${
pull_request.html_url
}>\n\n${pull_request.body || "No description"}`
);
} else {
// No existing issue, create new thread
const forum = await discord.channels.fetch(FORUM_CHANNEL_ID);
await forum.threads.create({
name: `#${pull_request.number} - ${pull_request.title}`,
message: {
content: `**New Pull Request by ${sender.login}:** <${
pull_request.html_url
}>\n\n${pull_request.body || "No description"}`,
},
});
}
}
// Handle comments on both issues and PRs
else if (action === "created" && comment && issue) {
const thread = await findThreadByIssueNumber(issue.number);
if (thread) {
await thread.send(
`**${sender.login} commented:** <${comment.html_url}>\n\n${comment.body}`
);
}
}
// Handle closing/reopening/merging
else if (action === "closed" || action === "reopened") {
let thread;
if (pull_request) {
thread = await findThreadByIssueNumber(pull_request.number);
if (thread) {
if (action === "closed" && pull_request.merged) {
await thread.send(`**${sender.login} merged this PR**`);
const message = await getStarterMessage(thread);
if (message) {
await message.react(
message.guild.emojis.cache.find(
(emoji) => emoji.name === "pr_merged",
),
);
}
} else {
await thread.send(`**${sender.login} ${action} this PR**`);
}
}
} else if (issue) {
thread = await findThreadByIssueNumber(issue.number);
if (thread) {
await thread.send(`**${sender.login} ${action} this issue**`);
}
}
}
res.sendStatus(200);
} catch (error) {
console.error("Error:", error);
res.sendStatus(500);
}
});
discord.login(BOT_TOKEN);
app.listen(3000, () => {
console.log("Webhook server running on port 3000");
});