Skip to content

Commit 01578dc

Browse files
authored
Merge pull request #1 from th30d4y/copilot/create-github-actions-workflow
Add org-wide issue/PR intake bot workflow with deduped welcome comments and owner email alerts
2 parents 3cbb0ad + db034cc commit 01578dc

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

.github/workflows/org-bot.yml

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Organization Issue/PR Bot
2+
3+
on:
4+
issues:
5+
types: [opened]
6+
pull_request:
7+
types: [opened]
8+
9+
permissions:
10+
contents: read
11+
issues: write
12+
pull-requests: write
13+
members: read
14+
15+
jobs:
16+
welcome-and-notify:
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Build event metadata
20+
id: metadata
21+
uses: actions/github-script@v7
22+
with:
23+
script: |
24+
try {
25+
const payload = context.payload;
26+
if (context.eventName === "issues" && payload.issue) {
27+
core.setOutput("type", "Issue");
28+
core.setOutput("number", String(payload.issue.number));
29+
core.setOutput("title", payload.issue.title || "(no title)");
30+
core.setOutput("url", payload.issue.html_url);
31+
return;
32+
}
33+
34+
if (context.eventName === "pull_request" && payload.pull_request) {
35+
core.setOutput("type", "Pull Request");
36+
core.setOutput("number", String(payload.pull_request.number));
37+
core.setOutput("title", payload.pull_request.title || "(no title)");
38+
core.setOutput("url", payload.pull_request.html_url);
39+
return;
40+
}
41+
42+
core.setFailed(`Unsupported event payload for "${context.eventName}".`);
43+
} catch (error) {
44+
core.setFailed(`Failed to build event metadata: ${error.message}`);
45+
}
46+
47+
- name: Post welcome comment
48+
uses: actions/github-script@v7
49+
env:
50+
BOT_MESSAGE: |
51+
👋 Hello! Thanks for your contribution. We will review this and get back to you as soon as possible. — th30d4y
52+
with:
53+
script: |
54+
const message = process.env.BOT_MESSAGE.trim();
55+
const issueNumber = context.issue.number;
56+
const owner = context.repo.owner;
57+
const repo = context.repo.repo;
58+
59+
try {
60+
const comments = await github.paginate(github.rest.issues.listComments, {
61+
owner,
62+
repo,
63+
issue_number: issueNumber,
64+
per_page: 100
65+
});
66+
67+
const normalize = (text) => (text || "").replace(/\r\n/g, "\n").trim();
68+
const expected = normalize(message);
69+
const alreadyCommented = comments.some((comment) => {
70+
if (!comment?.body) return false;
71+
if (!comment.user?.type?.toLowerCase().includes('bot')) return false;
72+
return normalize(comment.body) === expected;
73+
});
74+
75+
if (alreadyCommented) {
76+
core.info(`Skipping comment: matching bot comment already exists on #${issueNumber}`);
77+
return;
78+
}
79+
80+
await github.rest.issues.createComment({
81+
owner,
82+
repo,
83+
issue_number: issueNumber,
84+
body: message
85+
});
86+
} catch (error) {
87+
core.setFailed(`Failed to post welcome comment: ${error.message}`);
88+
}
89+
90+
- name: Resolve organization owner recipients
91+
id: recipients
92+
uses: actions/github-script@v7
93+
with:
94+
result-encoding: string
95+
script: |
96+
const org = context.repo.owner;
97+
98+
try {
99+
const admins = await github.paginate(github.rest.orgs.listMembers, {
100+
org,
101+
role: "admin",
102+
per_page: 100
103+
});
104+
105+
if (!admins.length) {
106+
core.warning(`No organization owners/admins found for org "${org}".`);
107+
return "";
108+
}
109+
110+
const recipientSet = new Set(
111+
admins.map((admin) => `${admin.id}+${admin.login}@users.noreply.github.com`)
112+
);
113+
114+
const recipients = [...recipientSet].join(",");
115+
core.info(`Resolved ${recipientSet.size} organization owner recipient(s).`);
116+
return recipients;
117+
} catch (error) {
118+
core.setFailed(`Failed to resolve organization owners: ${error.message}`);
119+
return "";
120+
}
121+
122+
- name: Send owner notification email
123+
if: ${{ steps.recipients.outputs.result != '' }}
124+
uses: dawidd6/action-send-mail@v3
125+
with:
126+
server_address: smtp.gmail.com
127+
server_port: 465
128+
secure: true
129+
username: ${{ secrets.EMAIL_USER }}
130+
password: ${{ secrets.EMAIL_PASS }}
131+
subject: "[${{ github.repository }}] New ${{ steps.metadata.outputs.type }} Opened: ${{ steps.metadata.outputs.title }}"
132+
to: ${{ steps.recipients.outputs.result }}
133+
from: th30d4y Bot <${{ secrets.EMAIL_USER }}>
134+
html_body: |
135+
<h3>📣 New ${{ steps.metadata.outputs.type }} Created</h3>
136+
<p><strong>Repository:</strong> <code>${{ github.repository }}</code></p>
137+
<p><strong>Type:</strong> ${{ steps.metadata.outputs.type }}</p>
138+
<p><strong>Title:</strong> ${{ steps.metadata.outputs.title }}</p>
139+
<p><strong>URL:</strong> <a href="${{ steps.metadata.outputs.url }}">${{ steps.metadata.outputs.url }}</a></p>
140+
<hr />
141+
<p>Automated notification by <strong>th30d4y</strong>.</p>
142+
143+
- name: Fail when email was not sent
144+
if: ${{ steps.recipients.outputs.result == '' }}
145+
shell: bash
146+
run: |
147+
echo "No recipients resolved, email notification was not sent." >&2
148+
exit 1

0 commit comments

Comments
 (0)