Skip to content

Commit 278e99b

Browse files
committed
Initial commit
1 parent 02f7214 commit 278e99b

File tree

5 files changed

+149
-0
lines changed

5 files changed

+149
-0
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Deploy Cloudflare Worker
2+
on:
3+
push:
4+
branches: main
5+
repository_dispatch:
6+
7+
jobs:
8+
cloudflare-worker:
9+
name: Deploy Cloudflare Worker
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Get Wrangler version
14+
id: wrangler_version
15+
run: |
16+
echo "npm=$(jq -r .dependencies.wrangler package.json | sed 's/\^//')" >> "$GITHUB_OUTPUT"
17+
- name: Deploy Cloudflare Worker
18+
uses: cloudflare/wrangler-action@v3
19+
with:
20+
apiToken: ${{ secrets.CF_API_TOKEN }}
21+
accountId: ${{ secrets.CF_ACCOUNT_ID }}
22+
wranglerVersion: ${{ steps.wrangler_version.outputs.npm }}
23+
secrets: |
24+
API_SECRET
25+
SLACK_WEBHOOK
26+
env:
27+
API_SECRET: ${{ secrets.API_SECRET }}
28+
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
package-lock.json
3+
.dev.vars

index.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Buffer } from 'node:buffer';
2+
3+
// See https://docs.github.com/en/webhooks/webhook-events-and-payloads
4+
function buildMessage(event) {
5+
const messageBlock = {
6+
type: 'section',
7+
text: { type: 'mrkdwn' },
8+
accessory: { type: 'image' },
9+
};
10+
11+
let user, type;
12+
13+
if (['member_added', 'member_removed'].includes(event.action)) {
14+
user = event.membership.user;
15+
type = `the ${event.organization.login} organisation`;
16+
} else if (['added', 'removed'].includes(event.action)) {
17+
user = event.member;
18+
type = `the <${event.repository.html_url}|${event.repository.full_name}> repository`;
19+
}
20+
21+
if (user) {
22+
messageBlock.accessory.image_url = user.avatar_url;
23+
messageBlock.accessory.alt_text = `${user.login} on GitHub`;
24+
messageBlock.text.text = `*<${user.html_url}|${user.login}>* `;
25+
26+
switch (event.action) {
27+
case 'member_added':
28+
case 'added':
29+
messageBlock.text.text += 'was added to ' + type;
30+
break;
31+
32+
case 'member_removed':
33+
case 'removed':
34+
if (user.login == event.sender.login) {
35+
messageBlock.text.text += 'removed themselves from ' + type;
36+
} else {
37+
messageBlock.text.text += 'was removed from ' + type;
38+
}
39+
break;
40+
}
41+
42+
if (event.action.includes('added') || user.login != event.sender.login) {
43+
messageBlock.text.text += ` by <${event.sender.html_url}|${event.sender.login}>`;
44+
}
45+
46+
return {
47+
text: messageBlock.text.text.replace(/<[^|]*\|([^>]*)>/g, '$1'),
48+
blocks: [messageBlock],
49+
};
50+
}
51+
}
52+
53+
// https://docs.github.com/en/webhooks/using-webhooks/validating-webhook-deliveries
54+
async function checkSignature(secret, signature, payload) {
55+
if (!signature) { return 'Missing signature'; }
56+
57+
// sha256=xxx
58+
let [alg, sig, ...extra] = signature.split('=');
59+
if (alg != 'sha256' || !sig || extra.length) { return 'Malformed signature'; }
60+
61+
const encoder = new TextEncoder();
62+
63+
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), {
64+
name: 'HMAC',
65+
hash: {name: 'SHA-256'},
66+
}, false, ['verify']);
67+
68+
const signatureBytes = Buffer.from(sig, 'hex');
69+
const data = encoder.encode(payload);
70+
const verify = await crypto.subtle.verify('HMAC', key, signatureBytes, data);
71+
if (!verify) { return 'Invalid signature'; }
72+
}
73+
74+
export default {
75+
async fetch(request, env, ctx) {
76+
const body = await request.text();
77+
const error = await checkSignature(
78+
env.API_SECRET,
79+
request.headers.get('x-hub-signature-256') || '',
80+
body
81+
);
82+
if (error) { return new Response(error, {status: 401}); }
83+
84+
const event = JSON.parse(body);
85+
const message = buildMessage(event);
86+
87+
if (message && env.SLACK_WEBHOOK) {
88+
let response = await fetch(env.SLACK_WEBHOOK, {
89+
method: 'POST',
90+
headers: {
91+
'content-type': 'application/json',
92+
},
93+
body: JSON.stringify(message),
94+
});
95+
96+
return new Response(response.body, {
97+
status: response.status,
98+
statusText: response.statusText,
99+
headers: response.headers,
100+
});
101+
}
102+
103+
return new Response('KO');
104+
},
105+
};

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"main": "index.js",
3+
"dependencies": {
4+
"wrangler": "^3.79.0"
5+
},
6+
"scripts": {
7+
"dev": "wrangler dev"
8+
}
9+
}

wrangler.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
name = "github-member-log"
2+
main = "index.js"
3+
compatibility_date = "2024-10-01"
4+
compatibility_flags = [ "nodejs_compat" ]

0 commit comments

Comments
 (0)