package require http
package require json
# Configuration
set webhook_secret "your_webhook_secret_here" ;# Replace with your GitHub webhook secret
set channel "#yourchannel" ;# Replace with your IRC channel
set port 5000 ;# Port for the webhook server
set bind_addr "0.0.0.0" ;# Listen on all interfaces
# Utility to compute HMAC-SHA256 for webhook signature verification
proc compute_hmac {secret data} {
set hmac [::sha256::hmac $secret $data]
return [binary encode hex $hmac]
}
# Verify GitHub webhook signature
proc verify_signature {payload signature} {
global webhook_secret
if {$signature == ""} {
return 0
}
set expected_signature [compute_hmac $webhook_secret $payload]
return [expr {$signature == "sha256=$expected_signature"}]
}
# Handle incoming webhook requests
proc webhook_handler {sock addr port} {
global channel webhook_secret
# Read the request
set request [http::geturl "http://$addr:$port" -socket $sock -timeout 10000]
set headers [http::meta $request]
set payload [http::data $request]
http::cleanup $request
# Get the GitHub event type and signature
set event [dict get $headers X-GitHub-Event]
set signature [dict get $headers X-Hub-Signature-256]
# Verify signature
if {![verify_signature $payload $signature]} {
putserv "PRIVMSG $channel :\[Webhook\] Invalid signature received."
return
}
# Parse JSON payload
if {[catch {set data [::json::json2dict $payload]} err]} {
putserv "PRIVMSG $channel :\[Webhook\] Error parsing JSON: $err"
return
}
# Handle push events
if {$event == "push"} {
set repo [dict get $data repository full_name]
set pusher [dict get $data pusher name]
set commits [dict get $data commits]
foreach commit $commits {
set commit_id [string range [dict get $commit id] 0 6]
set message [lindex [split [dict get $commit message] "\n"] 0]
set url [dict get $commit url]
set author [dict get $commit author name]
putserv "PRIVMSG $channel :\[${repo}\] New commit by ${author} (${commit_id}): ${message} ${url}"
}
}
# Handle issue events (only when opened)
if {$event == "issues" && [dict get $data action] == "opened"} {
set repo [dict get $data repository full_name]
set issue [dict get $data issue]
set issue_number [dict get $issue number]
set title [dict get $issue title]
set url [dict get $issue html_url]
set user [dict get $issue user login]
putserv "PRIVMSG $channel :\[${repo}\] New issue #${issue_number} opened by ${user}: ${title} ${url}"
}
# Send HTTP response
puts $sock "HTTP/1.1 200 OK"
puts $sock "Content-Type: text/plain"
puts $sock "Content-Length: 2"
puts $sock ""
puts $sock "OK"
close $sock
}
# Start the webhook server
proc start_webhook_server {} {
global port bind_addr
if {[catch {socket -server webhook_handler $bind_addr:$port} err]} {
putlog "Error starting webhook server: $err"
} else {
putlog "Webhook server started on port $port"
}
}
# Load the sha256 package (included with Eggdrop)
package require sha256
# Start the server when the script is loaded
start_webhook_server
putlog "GitHub Webhook Relay loaded."