From 7f0af81330e30442de01bf83a5da525430c7a912 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Jul 2016 15:11:50 +0100 Subject: [PATCH 1/4] Add very noddy github service. Add owner/repo#11 expander. --- src/github.com/matrix-org/go-neb/goneb.go | 1 + .../go-neb/services/github/github.go | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/github.com/matrix-org/go-neb/services/github/github.go diff --git a/src/github.com/matrix-org/go-neb/goneb.go b/src/github.com/matrix-org/go-neb/goneb.go index 0d7c611..062970b 100644 --- a/src/github.com/matrix-org/go-neb/goneb.go +++ b/src/github.com/matrix-org/go-neb/goneb.go @@ -6,6 +6,7 @@ import ( "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/server" _ "github.com/matrix-org/go-neb/services/echo" + _ "github.com/matrix-org/go-neb/services/github" _ "github.com/mattn/go-sqlite3" "net/http" _ "net/http/pprof" 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 new file mode 100644 index 0000000..8c20fab --- /dev/null +++ b/src/github.com/matrix-org/go-neb/services/github/github.go @@ -0,0 +1,52 @@ +package services + +import ( + "github.com/matrix-org/go-neb/database" + "github.com/matrix-org/go-neb/matrix" + "github.com/matrix-org/go-neb/plugin" + "regexp" + "strings" +) + +type githubService struct { + id string + UserID string + Rooms []string +} + +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) Plugin(roomID string) plugin.Plugin { + return plugin.Plugin{ + Commands: []plugin.Command{ + plugin.Command{ + Path: []string{"github"}, + Command: func(roomID, userID string, args []string) (interface{}, error) { + return &matrix.TextMessage{"m.notice", strings.Join(args, " ")}, nil + }, + }, + }, + Expansions: []plugin.Expansion{ + plugin.Expansion{ + // E.g. owner/repo#11 (issue/PR numbers) + Regexp: regexp.MustCompile("[A-z0-9-_]+/[A-z0-9-_]+#[0-9]+"), + Expand: func(roomID, matchingText string) interface{} { + // get the issue/PR number + repoAndNum := strings.Split(matchingText, "#") + return &matrix.TextMessage{ + "m.notice", + "Repo: " + repoAndNum[0] + " Num: " + repoAndNum[1], + } + }, + }, + }, + } +} + +func init() { + database.RegisterService(func(serviceID string) database.Service { + return &githubService{id: serviceID} + }) +} From 01e51f36a98b3d94e5e19527b5cc972961cfacb3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Jul 2016 16:33:12 +0100 Subject: [PATCH 2/4] Add github issue expansion for things that look like owner/repo#11 --- .../go-neb/services/github/github.go | 62 +++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) 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 8c20fab..6222e02 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 @@ -1,13 +1,22 @@ package services import ( + "fmt" + log "github.com/Sirupsen/logrus" + "github.com/google/go-github/github" "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/matrix" "github.com/matrix-org/go-neb/plugin" + "golang.org/x/oauth2" "regexp" + "strconv" "strings" ) +// Matches alphanumeric then a /, then more alphanumeric then a #, then a number. +// E.g. owner/repo#11 (issue/PR numbers) - Captured groups for owner/repo/number +const ownerRepoIssueRegex = "([A-z0-9-_]+)/([A-z0-9-_]+)#([0-9]+)" + type githubService struct { id string UserID string @@ -30,14 +39,29 @@ func (s *githubService) Plugin(roomID string) plugin.Plugin { }, Expansions: []plugin.Expansion{ plugin.Expansion{ - // E.g. owner/repo#11 (issue/PR numbers) - Regexp: regexp.MustCompile("[A-z0-9-_]+/[A-z0-9-_]+#[0-9]+"), + Regexp: regexp.MustCompile(ownerRepoIssueRegex), Expand: func(roomID, matchingText string) interface{} { - // get the issue/PR number - repoAndNum := strings.Split(matchingText, "#") + cli := githubClient("") + owner, repo, num, err := ownerRepoNumberFromText(matchingText) + if err != nil { + log.WithError(err).WithField("text", matchingText).Print( + "Failed to extract owner,repo,number from matched string") + return nil + } + + i, _, err := cli.Issues.Get(owner, repo, num) + if err != nil { + log.WithError(err).WithFields(log.Fields{ + "owner": owner, + "repo": repo, + "number": num, + }).Print("Failed to fetch issue") + return nil + } + return &matrix.TextMessage{ "m.notice", - "Repo: " + repoAndNum[0] + " Num: " + repoAndNum[1], + fmt.Sprintf("%s : %s", *i.HTMLURL, *i.Title), } }, }, @@ -45,6 +69,34 @@ func (s *githubService) Plugin(roomID string) plugin.Plugin { } } +func githubClient(token string) *github.Client { + var tokenSource oauth2.TokenSource + if token != "" { + tokenSource = oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + } + httpCli := oauth2.NewClient(oauth2.NoContext, tokenSource) + return github.NewClient(httpCli) +} + +// ownerRepoNumberFromText parses a GH issue string that looks like 'owner/repo#11' +// into its constituient parts. Returns: owner, repo, issue#. +func ownerRepoNumberFromText(ownerRepoNumberText string) (string, string, int, error) { + // TODO: cache this? + re := regexp.MustCompile(ownerRepoIssueRegex) + // [full_string, owner, repo, issue_number] + groups := re.FindStringSubmatch(ownerRepoNumberText) + if len(groups) != 4 { + return "", "", 0, fmt.Errorf("No match found for '%s'", ownerRepoNumberText) + } + num, err := strconv.Atoi(groups[3]) + if err != nil { + return "", "", 0, err + } + return groups[1], groups[2], num, nil +} + func init() { database.RegisterService(func(serviceID string) database.Service { return &githubService{id: serviceID} From 9cdd546ba6443b2d4f62652673ed9a0e4d8ec4f5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 29 Jul 2016 16:45:11 +0100 Subject: [PATCH 3/4] Remove spurious !github command --- .../matrix-org/go-neb/services/github/github.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) 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 6222e02..1ed5f88 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 @@ -10,7 +10,6 @@ import ( "golang.org/x/oauth2" "regexp" "strconv" - "strings" ) // Matches alphanumeric then a /, then more alphanumeric then a #, then a number. @@ -29,14 +28,7 @@ func (s *githubService) ServiceType() string { return "github" } func (s *githubService) RoomIDs() []string { return s.Rooms } func (s *githubService) Plugin(roomID string) plugin.Plugin { return plugin.Plugin{ - Commands: []plugin.Command{ - plugin.Command{ - Path: []string{"github"}, - Command: func(roomID, userID string, args []string) (interface{}, error) { - return &matrix.TextMessage{"m.notice", strings.Join(args, " ")}, nil - }, - }, - }, + Commands: []plugin.Command{}, Expansions: []plugin.Expansion{ plugin.Expansion{ Regexp: regexp.MustCompile(ownerRepoIssueRegex), @@ -69,6 +61,9 @@ func (s *githubService) Plugin(roomID string) plugin.Plugin { } } +// githubClient returns a github Client which can perform Github API operations. +// If `token` is empty, a non-authenticated client will be created. This should be +// used sparingly where possible as you only get 60 requests/hour like that (IP locked). func githubClient(token string) *github.Client { var tokenSource oauth2.TokenSource if token != "" { From d6b946f200b4300c117fdadf3aca4f8dba11dc92 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 1 Aug 2016 11:58:41 +0100 Subject: [PATCH 4/4] Make regexp top-level as per PR comments --- .../matrix-org/go-neb/services/github/github.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) 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 1ed5f88..df58df0 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 @@ -14,7 +14,7 @@ import ( // Matches alphanumeric then a /, then more alphanumeric then a #, then a number. // E.g. owner/repo#11 (issue/PR numbers) - Captured groups for owner/repo/number -const ownerRepoIssueRegex = "([A-z0-9-_]+)/([A-z0-9-_]+)#([0-9]+)" +var ownerRepoIssueRegex = regexp.MustCompile("([A-z0-9-_]+)/([A-z0-9-_]+)#([0-9]+)") type githubService struct { id string @@ -31,7 +31,7 @@ func (s *githubService) Plugin(roomID string) plugin.Plugin { Commands: []plugin.Command{}, Expansions: []plugin.Expansion{ plugin.Expansion{ - Regexp: regexp.MustCompile(ownerRepoIssueRegex), + Regexp: ownerRepoIssueRegex, Expand: func(roomID, matchingText string) interface{} { cli := githubClient("") owner, repo, num, err := ownerRepoNumberFromText(matchingText) @@ -78,10 +78,8 @@ func githubClient(token string) *github.Client { // ownerRepoNumberFromText parses a GH issue string that looks like 'owner/repo#11' // into its constituient parts. Returns: owner, repo, issue#. func ownerRepoNumberFromText(ownerRepoNumberText string) (string, string, int, error) { - // TODO: cache this? - re := regexp.MustCompile(ownerRepoIssueRegex) // [full_string, owner, repo, issue_number] - groups := re.FindStringSubmatch(ownerRepoNumberText) + groups := ownerRepoIssueRegex.FindStringSubmatch(ownerRepoNumberText) if len(groups) != 4 { return "", "", 0, fmt.Errorf("No match found for '%s'", ownerRepoNumberText) }