diff --git a/src/github.com/matrix-org/go-neb/api.go b/src/github.com/matrix-org/go-neb/api.go index 3699608..8f4f1c9 100644 --- a/src/github.com/matrix-org/go-neb/api.go +++ b/src/github.com/matrix-org/go-neb/api.go @@ -44,7 +44,8 @@ func (*configureAuthHandler) OnIncomingRequest(req *http.Request) (interface{}, } type webhookHandler struct { - db *database.ServiceDB + db *database.ServiceDB + clients *clients.Clients } func (wh *webhookHandler) handle(w http.ResponseWriter, req *http.Request) { @@ -57,7 +58,14 @@ func (wh *webhookHandler) handle(w http.ResponseWriter, req *http.Request) { w.WriteHeader(404) return } - service.OnReceiveWebhook(w, req) + cli, err := wh.clients.Client(service.ServiceUserID()) + if err != nil { + log.WithError(err).WithField("user_id", service.ServiceUserID()).Print( + "Failed to retrieve matrix client instance") + w.WriteHeader(500) + return + } + service.OnReceiveWebhook(w, req, cli) } type configureClientHandler struct { diff --git a/src/github.com/matrix-org/go-neb/clients/clients.go b/src/github.com/matrix-org/go-neb/clients/clients.go index 8aab734..1f6684f 100644 --- a/src/github.com/matrix-org/go-neb/clients/clients.go +++ b/src/github.com/matrix-org/go-neb/clients/clients.go @@ -22,7 +22,7 @@ type Clients struct { func New(db *database.ServiceDB) *Clients { clients := &Clients{ db: db, - clients: make(map[string]clientEntry), + clients: make(map[string]clientEntry), // user_id => clientEntry } return clients } diff --git a/src/github.com/matrix-org/go-neb/goneb.go b/src/github.com/matrix-org/go-neb/goneb.go index ca45dc8..8e983a7 100644 --- a/src/github.com/matrix-org/go-neb/goneb.go +++ b/src/github.com/matrix-org/go-neb/goneb.go @@ -35,7 +35,7 @@ func main() { http.Handle("/admin/configureClient", server.MakeJSONAPI(&configureClientHandler{db: db, clients: clients})) http.Handle("/admin/configureService", server.MakeJSONAPI(&configureServiceHandler{db: db, clients: clients})) http.Handle("/admin/configureAuth", server.MakeJSONAPI(&configureAuthHandler{db: db})) - wh := &webhookHandler{db: db} + wh := &webhookHandler{db: db, clients: clients} http.HandleFunc("/services/hooks/", wh.handle) http.ListenAndServe(bindAddress, nil) diff --git a/src/github.com/matrix-org/go-neb/services/echo/echo.go b/src/github.com/matrix-org/go-neb/services/echo/echo.go index 11655d8..46dc4d2 100644 --- a/src/github.com/matrix-org/go-neb/services/echo/echo.go +++ b/src/github.com/matrix-org/go-neb/services/echo/echo.go @@ -30,7 +30,7 @@ func (e *echoService) Plugin(roomID string) plugin.Plugin { }, } } -func (e *echoService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request) { +func (e *echoService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) { w.WriteHeader(200) // Do nothing } diff --git a/src/github.com/matrix-org/go-neb/services/github/github.go b/src/github.com/matrix-org/go-neb/services/github/github.go index 0796a79..d194702 100644 --- a/src/github.com/matrix-org/go-neb/services/github/github.go +++ b/src/github.com/matrix-org/go-neb/services/github/github.go @@ -21,13 +21,19 @@ var ownerRepoIssueRegex = regexp.MustCompile("([A-z0-9-_]+)/([A-z0-9-_]+)#([0-9] type githubService struct { id string UserID string - Rooms []string + Rooms map[string][]string // room_id => ["push","issue","pull_request"] } func (s *githubService) ServiceUserID() string { return s.UserID } func (s *githubService) ServiceID() string { return s.id } func (s *githubService) ServiceType() string { return "github" } -func (s *githubService) RoomIDs() []string { return s.Rooms } +func (s *githubService) RoomIDs() []string { + var keys []string + for k := range s.Rooms { + keys = append(keys, k) + } + return keys +} func (s *githubService) Plugin(roomID string) plugin.Plugin { return plugin.Plugin{ Commands: []plugin.Command{}, @@ -62,9 +68,36 @@ func (s *githubService) Plugin(roomID string) plugin.Plugin { }, } } -func (s *githubService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request) { - // defer entirely to the webhook package - webhook.OnReceiveRequest(w, req, "") +func (s *githubService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) { + evType, repo, msg, err := webhook.OnReceiveRequest(req, "") + if err != nil { + w.WriteHeader(err.Code) + return + } + + for roomID, notif := range s.Rooms { + notifyRoom := false + for _, notifyType := range notif { + if evType == notifyType { + notifyRoom = true + break + } + } + if notifyRoom { + log.WithFields(log.Fields{ + "type": evType, + "msg": msg, + "repo": repo, + "room_id": roomID, + }).Print("Sending notification to room") + _, e := cli.SendMessageEvent(roomID, "m.room.message", msg) + if e != nil { + log.WithError(e).WithField("room_id", roomID).Print( + "Failed to send notification to room.") + } + } + } + w.WriteHeader(200) } // githubClient returns a github Client which can perform Github API operations. diff --git a/src/github.com/matrix-org/go-neb/services/github/webhook/webhook.go b/src/github.com/matrix-org/go-neb/services/github/webhook/webhook.go index 3def2ca..8e03d4c 100644 --- a/src/github.com/matrix-org/go-neb/services/github/webhook/webhook.go +++ b/src/github.com/matrix-org/go-neb/services/github/webhook/webhook.go @@ -8,23 +8,26 @@ import ( "fmt" log "github.com/Sirupsen/logrus" "github.com/google/go-github/github" + "github.com/matrix-org/go-neb/errors" + "github.com/matrix-org/go-neb/matrix" "html" "io/ioutil" "net/http" "strings" ) -// OnReceiveRequest processes incoming github webhook requests. The secretToken -// parameter is optional. -func OnReceiveRequest(w http.ResponseWriter, r *http.Request, secretToken string) { +// OnReceiveRequest processes incoming github webhook requests and returns a +// matrix message to send, along with parsed repo information. +// The secretToken, if supplied, will be used to verify the request is from +// Github. If it isn't, an error is returned. +func OnReceiveRequest(r *http.Request, secretToken string) (string, *github.Repository, *matrix.HTMLMessage, *errors.HTTPError) { // Verify the HMAC signature if NEB was configured with a secret token eventType := r.Header.Get("X-GitHub-Event") signatureSHA1 := r.Header.Get("X-Hub-Signature") content, err := ioutil.ReadAll(r.Body) if err != nil { log.WithError(err).Print("Failed to read Github webhook body") - w.WriteHeader(400) - return + return "", nil, nil, &errors.HTTPError{nil, "Failed to parse body", 400} } // Verify request if a secret token has been supplied. if secretToken != "" { @@ -34,16 +37,14 @@ func OnReceiveRequest(w http.ResponseWriter, r *http.Request, secretToken string if err != nil { log.WithError(err).WithField("X-Hub-Signature", sigHex).Print( "Failed to decode signature as hex.") - w.WriteHeader(400) - return + return "", nil, nil, &errors.HTTPError{nil, "Failed to decode signature", 400} } if !checkMAC([]byte(content), sigBytes, []byte(secretToken)) { log.WithFields(log.Fields{ "X-Hub-Signature": signatureSHA1, }).Print("Received Github event which failed MAC check.") - w.WriteHeader(403) - return + return "", nil, nil, &errors.HTTPError{nil, "Bad signature", 403} } } @@ -55,16 +56,11 @@ func OnReceiveRequest(w http.ResponseWriter, r *http.Request, secretToken string htmlStr, repo, err := parseGithubEvent(eventType, content) if err != nil { log.WithError(err).Print("Failed to parse github event") - w.WriteHeader(500) - return + return "", nil, nil, &errors.HTTPError{nil, "Failed to parse github event", 500} } - if err := handleWebhookEvent(eventType, htmlStr, repo); err != nil { - log.WithError(err).Print("Failed to handle Github webhook event") - w.WriteHeader(500) - return - } - w.WriteHeader(200) + msg := matrix.GetHTMLMessage("m.notice", htmlStr) + return eventType, repo, &msg, nil } // checkMAC reports whether messageMAC is a valid HMAC tag for message. @@ -122,10 +118,6 @@ func parseGithubEvent(eventType string, data []byte) (string, *github.Repository return "", nil, fmt.Errorf("Unrecognized event type") } -func handleWebhookEvent(eventType string, htmlStr string, repo *github.Repository) error { - return nil -} - func pullRequestHTMLMessage(p github.PullRequestEvent) string { var actionTarget string if p.PullRequest.Assignee != nil && p.PullRequest.Assignee.Login != nil { diff --git a/src/github.com/matrix-org/go-neb/types/types.go b/src/github.com/matrix-org/go-neb/types/types.go index 1f0f6c9..c63a15a 100644 --- a/src/github.com/matrix-org/go-neb/types/types.go +++ b/src/github.com/matrix-org/go-neb/types/types.go @@ -3,6 +3,7 @@ package types import ( "encoding/json" "errors" + "github.com/matrix-org/go-neb/matrix" "github.com/matrix-org/go-neb/plugin" "net/http" "net/url" @@ -33,7 +34,7 @@ type Service interface { ServiceType() string RoomIDs() []string Plugin(roomID string) plugin.Plugin - OnReceiveWebhook(w http.ResponseWriter, req *http.Request) + OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) } var servicesByType = map[string]func(string) Service{}