Skip to content

Commit 203ff94

Browse files
author
Baudbot
committed
bridge: add GitHub webhook event dispatch and formatting
- New github-events.mjs module with pure formatting/filtering functions - Add 'github' case to processPulledMessage() generic envelope switch - Format pull_request, pull_request_review, issue_comment, check_suite, check_run, push events with security boundary wrapping - Filter noise: skip baudbot-agent events, configurable GITHUB_IGNORED_USERS, skip check_suite/check_run lifecycle events, skip PR synchronize - Handle ping (ack only) and unknown event types gracefully - 45 tests covering formatting, filtering, boundary wrapping, edge cases - Backwards compatible: no changes to Slack message flow
1 parent caa669f commit 203ff94

5 files changed

Lines changed: 964 additions & 0 deletions

File tree

slack-bridge/broker-bridge.mjs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ import {
2323
createRateLimiter,
2424
sanitizeOutboundText,
2525
} from "./security.mjs";
26+
import {
27+
formatGitHubEvent,
28+
shouldSkipEvent,
29+
parseIgnoredUsers,
30+
extractActor,
31+
} from "./github-events.mjs";
2632
import {
2733
canonicalizeEnvelope,
2834
canonicalizeProtocolRequest,
@@ -135,6 +141,8 @@ if (ALLOWED_USERS.length === 0) {
135141
logWarn("⚠️ SLACK_ALLOWED_USERS not set — all workspace members can interact");
136142
}
137143

144+
const GITHUB_IGNORED_USERS = parseIgnoredUsers(process.env.GITHUB_IGNORED_USERS);
145+
138146
const slackRateLimiter = createRateLimiter({ maxRequests: 5, windowMs: 60_000 });
139147
const apiRateLimiter = createRateLimiter({ maxRequests: 30, windowMs: 60_000 });
140148

@@ -864,6 +872,46 @@ async function handleSlackPayload(slackEventEnvelopePayload) {
864872
return true;
865873
}
866874

875+
async function handleGitHubEvent(type, payload) {
876+
const actor = extractActor(type, payload);
877+
const repo = payload?.repository?.full_name || "unknown/repo";
878+
logInfo(`🐙 github event: ${type} (action: ${payload?.action || "n/a"}) repo: ${repo} actor: ${actor || "n/a"}`);
879+
880+
// Filtering: skip noisy or self-generated events
881+
const skipReason = shouldSkipEvent(type, payload, GITHUB_IGNORED_USERS);
882+
if (skipReason) {
883+
logInfo(` ↳ skipping: ${skipReason}`);
884+
return true;
885+
}
886+
887+
const { message, isPing, isUnknown } = formatGitHubEvent(type, payload);
888+
889+
if (isPing) {
890+
logInfo(" ↳ ping event — webhook configured successfully");
891+
return true;
892+
}
893+
894+
if (isUnknown) {
895+
logWarn(` ↳ unhandled github event type: ${type} — forwarding minimal summary`);
896+
}
897+
898+
if (!message) {
899+
logWarn(` ↳ formatter returned no message for ${type} — skipping`);
900+
return true;
901+
}
902+
903+
refreshSocket();
904+
const currentSocket = socketPath;
905+
if (!currentSocket) {
906+
logError("🔌 no pi socket found for github event — agent may not be running");
907+
return true;
908+
}
909+
910+
await enqueue(() => sendToAgent(currentSocket, message));
911+
logInfo(` ↳ forwarded to agent`);
912+
return true;
913+
}
914+
867915
async function handleDashboardEvent(type, payload) {
868916
logInfo(`📊 dashboard event: ${type}`, JSON.stringify(payload).slice(0, 200));
869917
// TODO: implement dashboard event handling (env updates, config changes)
@@ -896,6 +944,8 @@ async function processPulledMessage(message) {
896944
switch (payload.source) {
897945
case "slack":
898946
return handleSlackPayload(payload.payload);
947+
case "github":
948+
return handleGitHubEvent(payload.type, payload.payload);
899949
case "dashboard":
900950
return handleDashboardEvent(payload.type, payload.payload);
901951
case "system":

0 commit comments

Comments
 (0)