package main import ( "fmt" "net/http" "strings" ) // githubParse formats GitHub webhook events. func githubParse(raw []byte, headers http.Header, meta Meta) (string, string, bool) { v, err := decodeJSON(raw) payload := asObject(v) if err != nil || payload.Len() == 0 { return "", "", false } // http.Header lookups are case-insensitive, which covers lowercase HTTP/2 names. event := headers.Get("X-GitHub-Event") if event == "" { event = "Unknown" } action := strOr(payload.Get("action"), "") repo := strOr(get(payload, "repository", "full_name"), "Unknown") sender := strOr(get(payload, "sender", "login"), "Unknown") eventSafe := h(event) repoSafe := h(repo) senderSafe := h(sender) icon := "🔔" var content string switch event { case "push": icon = "📤" ref := h(strOr(payload.Get("ref"), "Unknown")) commits, _ := payload.Get("commits").([]any) content = section("Push Details", table( row("Branch", ref), row("Commits", fmt.Sprint(len(commits))), row("Pusher", senderSafe), )) if len(commits) > 0 { var b strings.Builder for i, c := range commits { if i == 10 { break } msg := esc(strOr(get(c, "message"), "")) author := h(strOr(get(c, "author", "name"), "Unknown")) id := strOr(get(c, "id"), "") if len(id) > 7 { id = id[:7] } sha := h(id) url := strOr(get(c, "url"), "") shaHTML := `` + sha + `` if url != "" { shaHTML = `` + sha + `` } b.WriteString(`
` + shaHTML + ` ` + author + `
` + msg + `
`) } content += section("Commits", b.String()) } case "pull_request": icon = "🔀" pr := payload.Get("pull_request") content = section("Pull Request Details", table( row("Number", "#"+h(strOr(get(pr, "number"), "N/A"))), row("Title", h(strOr(get(pr, "title"), "N/A"))), row("State", h(strOr(get(pr, "state"), "N/A"))), row("Branch", h(strOr(get(pr, "head", "ref"), "N/A"))+" → "+h(strOr(get(pr, "base", "ref"), "N/A"))), row("URL", link(strOr(get(pr, "html_url"), "#"))), )) case "issues": icon = "📝" is := payload.Get("issue") content = section("Issue Details", table( row("Number", "#"+h(strOr(get(is, "number"), "N/A"))), row("Title", h(strOr(get(is, "title"), "N/A"))), row("State", h(strOr(get(is, "state"), "N/A"))), row("URL", link(strOr(get(is, "html_url"), "#"))), )) case "release": icon = "🚀" rel := payload.Get("release") content = section("Release Details", table( row("Tag", h(strOr(get(rel, "tag_name"), "N/A"))), row("Name", h(strOr(get(rel, "name"), "N/A"))), row("URL", link(strOr(get(rel, "html_url"), "#"))), )) default: content = section("Event Details", renderDataAsTable(payload, 0)) } actionDisplay := "" if action != "" { actionDisplay = " (" + h(action) + ")" } body := section("📋 Repository Information", table( row("Repository", repoSafe), row("Event", eventSafe), row("Sender", senderSafe), row("IP Address", h(meta.IP)), )) + content html := emailShell(icon+" GitHub: "+eventSafe+actionDisplay, h(meta.Date), body, "This GitHub webhook was automatically forwarded to your email address.") subject := fmt.Sprintf("%s GitHub %s%s - %s", icon, event, strings.ReplaceAll(actionDisplay, "'", "'"), repo) return html, subject, true }