From fd9e3a0957c4dcc1c05ffd2d38b144031c1c973a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 14:54:53 +0100 Subject: [PATCH 01/17] fix error wording Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 6 +++--- 1 file changed, 3 insertions(+), 3 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 866f5d5..9afa180 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 @@ -207,11 +207,11 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf }) if err != nil { - log.WithField("err", err).Print("Failed to create issue") + log.WithField("err", err).Print("Failed to create issue comment") if res == nil { - return nil, fmt.Errorf("Failed to create issue. Failed to connect to Github") + return nil, fmt.Errorf("Failed to create issue comment. Failed to connect to Github") } - return nil, fmt.Errorf("Failed to create issue. HTTP %d", res.StatusCode) + return nil, fmt.Errorf("Failed to create issue comment. HTTP %d", res.StatusCode) } return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Commented on issue: %s", *issueComment.HTMLURL)}, nil From aa364401122d29148d136f933c62318215e997da Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 15:34:56 +0100 Subject: [PATCH 02/17] add !github close command Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../go-neb/services/github/github.go | 127 ++++++++++++------ 1 file changed, 86 insertions(+), 41 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 9afa180..0038ec4 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 @@ -152,45 +152,10 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf `Usage: !github comment [owner/repo]#issue "comment text"`}, nil } - // We expect the args to look like: - // [ "[owner/repo]#issue", "comment" ] - // They can omit the owner/repo if there is a default one set. - // Look for a default if the first arg is just an issue number - ownerRepoIssueGroups := ownerRepoIssueRegexAnchored.FindStringSubmatch(args[0]) - - if len(ownerRepoIssueGroups) != 5 { - return &gomatrix.TextMessage{"m.notice", - `Usage: !github comment [owner/repo]#issue "comment text"`}, nil - } - - if ownerRepoIssueGroups[1] == "" { - // issue only match, this only works if there is a default repo - defaultRepo := s.defaultRepo(roomID) - if defaultRepo == "" { - return &gomatrix.TextMessage{"m.notice", - `Usage: !github comment [owner/repo]#issue "comment text"`}, nil - } - - segs := strings.Split(defaultRepo, "/") - if len(segs) != 2 { - return &gomatrix.TextMessage{"m.notice", - `Malformed default repo. Usage: !github comment [owner/repo]#issue "comment text"`}, nil - } - - // Fill in the missing fields in matching groups and fall through into ["foo/bar#11", "foo", "bar", "11"] - ownerRepoIssueGroups = []string{ - defaultRepo + ownerRepoIssueGroups[0], - defaultRepo, - segs[0], - segs[1], - ownerRepoIssueGroups[4], - } - } - - issueNum, err := strconv.Atoi(ownerRepoIssueGroups[4]) - if err != nil { - return &gomatrix.TextMessage{"m.notice", - `Malformed issue number. Usage: !github comment [owner/repo]#issue "comment text"`}, nil + // get owner,repo,issue,resp out of args[0] + owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, `!github comment [owner/repo]#issue "comment text"`) + if resp != nil { + return resp, nil } var comment *string @@ -202,7 +167,7 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf comment = &joinedComment } - issueComment, res, err := cli.Issues.CreateComment(ownerRepoIssueGroups[2], ownerRepoIssueGroups[3], issueNum, &gogithub.IssueComment{ + issueComment, res, err := cli.Issues.CreateComment(owner, repo, issueNum, &gogithub.IssueComment{ Body: comment, }) @@ -217,6 +182,79 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Commented on issue: %s", *issueComment.HTMLURL)}, nil } +func (s *Service) cmdGithubClose(roomID, userID string, args []string) (interface{}, error) { + cli, resp, err := s.requireGithubClientFor(userID) + if cli == nil { + return resp, err + } + if len(args) == 0 { + return &gomatrix.TextMessage{"m.notice", + `Usage: !github close [owner/repo]#issue`}, nil + } + + // get owner,repo,issue,resp out of args[0] + owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, `!github close [owner/repo]#issue`) + if resp != nil { + return resp, nil + } + + state := "closed" + issueComment, res, err := cli.Issues.Edit(owner, repo, issueNum, &gogithub.IssueRequest{ + State: &state, + }) + + if err != nil { + log.WithField("err", err).Print("Failed to close issue") + if res == nil { + return nil, fmt.Errorf("Failed to close issue. Failed to connect to Github") + } + return nil, fmt.Errorf("Failed to close issue. HTTP %d", res.StatusCode) + } + + return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Closed issue: %s", *issueComment.HTMLURL)}, nil +} + +func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo string, issueNum int, resp interface{}) { + // We expect the input to look like: + // "[owner/repo]#issue" + // They can omit the owner/repo if there is a default one set. + // Look for a default if the first arg is just an issue number + ownerRepoIssueGroups := ownerRepoIssueRegexAnchored.FindStringSubmatch(input) + + if len(ownerRepoIssueGroups) != 5 { + resp = &gomatrix.TextMessage{"m.notice", "Usage: " + usage} + return + } + + owner = ownerRepoIssueGroups[2] + repo = ownerRepoIssueGroups[3] + + var err error + if issueNum, err = strconv.Atoi(ownerRepoIssueGroups[4]); err != nil { + resp = &gomatrix.TextMessage{"m.notice", "Malformed issue number. Usage: " + usage} + return + } + + if ownerRepoIssueGroups[1] == "" { + // issue only match, this only works if there is a default repo + defaultRepo := s.defaultRepo(roomID) + if defaultRepo == "" { + resp = &gomatrix.TextMessage{"m.notice", "Usage: " + usage} + return + } + + segs := strings.Split(defaultRepo, "/") + if len(segs) != 2 { + resp = &gomatrix.TextMessage{"m.notice", "Malformed default repo. Usage: " + usage} + return + } + + owner = segs[0] + repo = segs[1] + } + return +} + func (s *Service) expandIssue(roomID, userID, owner, repo string, issueNum int) interface{} { cli := s.githubClientFor(userID, true) @@ -259,13 +297,20 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command { return s.cmdGithubComment(roomID, userID, args) }, }, + types.Command{ + Path: []string{"github", "close"}, + Command: func(roomID, userID string, args []string) (interface{}, error) { + return s.cmdGithubClose(roomID, userID, args) + }, + }, types.Command{ Path: []string{"github", "help"}, Command: func(roomID, userID string, args []string) (interface{}, error) { return &gomatrix.TextMessage{ "m.notice", fmt.Sprintf(`!github create owner/repo "title text" "description text"` + "\n" + - `!github comment [owner/repo]#issue "comment text"`), + `!github comment [owner/repo]#issue "comment text"` + "\n" + + `!github close [owner/repo]#issue`), }, nil }, }, From 3a046cf3ad2fc5683100b1cd3193bf3289bfda48 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 15:52:43 +0100 Subject: [PATCH 03/17] add !github react Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../go-neb/services/github/github.go | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) 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 0038ec4..ee62bd8 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 @@ -142,6 +142,35 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil } +func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interface{}, error) { + cli, resp, err := s.requireGithubClientFor(userID) + if cli == nil { + return resp, err + } + if len(args) < 2 { + return &gomatrix.TextMessage{"m.notice", + `Usage: !github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]`}, nil + } + + // get owner,repo,issue,resp out of args[0] + owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, `!github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]`) + if resp != nil { + return resp, nil + } + + _, res, err := cli.Reactions.CreateIssueReaction(owner, repo, issueNum, args[1]) + + if err != nil { + log.WithField("err", err).Print("Failed to react to issue") + if res == nil { + return nil, fmt.Errorf("Failed to react to issue. Failed to connect to Github") + } + return nil, fmt.Errorf("Failed to react to issue. HTTP %d", res.StatusCode) + } + + return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Reacted to issue with: %s", args[1])}, nil +} + func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) if cli == nil { @@ -291,6 +320,12 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command { return s.cmdGithubCreate(roomID, userID, args) }, }, + types.Command{ + Path: []string{"github", "react"}, + Command: func(roomID, userID string, args []string) (interface{}, error) { + return s.cmdGithubReact(roomID, userID, args) + }, + }, types.Command{ Path: []string{"github", "comment"}, Command: func(roomID, userID string, args []string) (interface{}, error) { @@ -309,6 +344,7 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command { return &gomatrix.TextMessage{ "m.notice", fmt.Sprintf(`!github create owner/repo "title text" "description text"` + "\n" + + `!github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]` + "\n" + `!github comment [owner/repo]#issue "comment text"` + "\n" + `!github close [owner/repo]#issue`), }, nil From c7ad22d4ee93d3675b0c44118e228e278f8ef6b3 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 15:59:56 +0100 Subject: [PATCH 04/17] DRY wrt cmd Usages Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../go-neb/services/github/github.go | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 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 ee62bd8..7d8b62d 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 @@ -77,14 +77,15 @@ func (s *Service) requireGithubClientFor(userID string) (cli *gogithub.Client, r return } +const cmdGithubCreateUsage = `!github create owner/repo "issue title" "description"` + func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) if cli == nil { return resp, err } if len(args) == 0 { - return &gomatrix.TextMessage{"m.notice", - `Usage: !github create owner/repo "issue title" "description"`}, nil + return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubCreateUsage}, nil } // We expect the args to look like: @@ -97,14 +98,12 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa // look for a default repo defaultRepo := s.defaultRepo(roomID) if defaultRepo == "" { - return &gomatrix.TextMessage{"m.notice", - `Usage: !github create owner/repo "issue title" "description"`}, nil + return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubCreateUsage}, nil } // default repo should pass the regexp ownerRepoGroups = ownerRepoRegex.FindStringSubmatch(defaultRepo) if len(ownerRepoGroups) == 0 { - return &gomatrix.TextMessage{"m.notice", - `Malformed default repo. Usage: !github create owner/repo "issue title" "description"`}, nil + return &gomatrix.TextMessage{"m.notice", "Malformed default repo. Usage: " + cmdGithubCreateUsage}, nil } // insert the default as the first arg to reuse the same indices @@ -142,18 +141,19 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil } +const cmdGithubReactUsage = `!github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]` + func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) if cli == nil { return resp, err } if len(args) < 2 { - return &gomatrix.TextMessage{"m.notice", - `Usage: !github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]`}, nil + return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubReactUsage}, nil } // get owner,repo,issue,resp out of args[0] - owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, `!github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]`) + owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, cmdGithubReactUsage) if resp != nil { return resp, nil } @@ -171,18 +171,19 @@ func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interfac return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Reacted to issue with: %s", args[1])}, nil } +const cmdGithubCommentUsage = `!github comment [owner/repo]#issue "comment text"` + func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) if cli == nil { return resp, err } if len(args) == 0 { - return &gomatrix.TextMessage{"m.notice", - `Usage: !github comment [owner/repo]#issue "comment text"`}, nil + return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubCommentUsage}, nil } // get owner,repo,issue,resp out of args[0] - owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, `!github comment [owner/repo]#issue "comment text"`) + owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, cmdGithubCommentUsage) if resp != nil { return resp, nil } @@ -211,18 +212,19 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Commented on issue: %s", *issueComment.HTMLURL)}, nil } +const cmdGithubCloseUsage = `!github close [owner/repo]#issue` + func (s *Service) cmdGithubClose(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) if cli == nil { return resp, err } if len(args) == 0 { - return &gomatrix.TextMessage{"m.notice", - `Usage: !github close [owner/repo]#issue`}, nil + return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubCloseUsage}, nil } // get owner,repo,issue,resp out of args[0] - owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, `!github close [owner/repo]#issue`) + owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, cmdGithubCloseUsage) if resp != nil { return resp, nil } @@ -343,10 +345,10 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command { Command: func(roomID, userID string, args []string) (interface{}, error) { return &gomatrix.TextMessage{ "m.notice", - fmt.Sprintf(`!github create owner/repo "title text" "description text"` + "\n" + - `!github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]` + "\n" + - `!github comment [owner/repo]#issue "comment text"` + "\n" + - `!github close [owner/repo]#issue`), + fmt.Sprintf(cmdGithubCreateUsage + "\n" + + cmdGithubReactUsage + "\n" + + cmdGithubCommentUsage + "\n" + + cmdGithubCloseUsage), }, nil }, }, From 2da64f9fbaa7a7c56835f525c77eeddb8395f278 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 17:19:22 +0100 Subject: [PATCH 05/17] add !github assign and tidy !github help generation code Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../go-neb/services/github/github.go | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 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 7d8b62d..2c4b9f0 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 @@ -212,6 +212,38 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Commented on issue: %s", *issueComment.HTMLURL)}, nil } +const cmdGithubAssignUsage = `!github assign [username]...` + +func (s *Service) cmdGithubAssign(roomID, userID string, args []string) (interface{}, error) { + cli, resp, err := s.requireGithubClientFor(userID) + if cli == nil { + return resp, err + } + if len(args) < 1 { + return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubAssignUsage}, nil + } else if len(args) < 2 { + return &gomatrix.TextMessage{"m.notice", "Needs at least one username. Usage: " + cmdGithubAssignUsage}, nil + } + + // get owner,repo,issue,resp out of args[0] + owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, cmdGithubAssignUsage) + if resp != nil { + return resp, nil + } + + issue, res, err := cli.Issues.AddAssignees(owner, repo, issueNum, args[1:]) + + if err != nil { + log.WithField("err", err).Print("Failed to add issue assignees") + if res == nil { + return nil, fmt.Errorf("Failed to add issue assignees. Failed to connect to Github") + } + return nil, fmt.Errorf("Failed to add issue assignees. HTTP %d", res.StatusCode) + } + + return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Added assignees to issue: %s", *issue.HTMLURL)}, nil +} + const cmdGithubCloseUsage = `!github close [owner/repo]#issue` func (s *Service) cmdGithubClose(roomID, userID string, args []string) (interface{}, error) { @@ -334,6 +366,12 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command { return s.cmdGithubComment(roomID, userID, args) }, }, + types.Command{ + Path: []string{"github", "assign"}, + Command: func(roomID, userID string, args []string) (interface{}, error) { + return s.cmdGithubAssign(roomID, userID, args) + }, + }, types.Command{ Path: []string{"github", "close"}, Command: func(roomID, userID string, args []string) (interface{}, error) { @@ -345,10 +383,13 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command { Command: func(roomID, userID string, args []string) (interface{}, error) { return &gomatrix.TextMessage{ "m.notice", - fmt.Sprintf(cmdGithubCreateUsage + "\n" + - cmdGithubReactUsage + "\n" + - cmdGithubCommentUsage + "\n" + - cmdGithubCloseUsage), + strings.Join([]string{ + cmdGithubCreateUsage, + cmdGithubReactUsage, + cmdGithubCommentUsage, + cmdGithubAssignUsage, + cmdGithubCloseUsage, + }, "\n"), }, nil }, }, From b6fe6092ab867d47c6a8487cad83c5115411125d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 17:42:50 +0100 Subject: [PATCH 06/17] fix cmd usage messages Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 4 ++-- 1 file changed, 2 insertions(+), 2 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 2c4b9f0..4902ad2 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 @@ -141,7 +141,7 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil } -const cmdGithubReactUsage = `!github react [owner/repo]#issue [+1|-1|laugh|confused|heart|hooray]` +const cmdGithubReactUsage = `!github react [owner/repo]#issue (+1|-1|laugh|confused|heart|hooray)` func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) @@ -212,7 +212,7 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Commented on issue: %s", *issueComment.HTMLURL)}, nil } -const cmdGithubAssignUsage = `!github assign [username]...` +const cmdGithubAssignUsage = `!github assign [owner/repo]#issue username [username] [...]` func (s *Service) cmdGithubAssign(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) From e8a4b1eda5be3fdc29fe3d2dca1712d5a4ac67f4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 17:56:29 +0100 Subject: [PATCH 07/17] error-check !github react before calling Github Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 3 +++ 1 file changed, 3 insertions(+) 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 4902ad2..f4a968c 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 @@ -151,6 +151,9 @@ func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interfac if len(args) < 2 { return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubReactUsage}, nil } + if args[1] != "+1" && args[1] != "-1" && args[1] != "laugh" && args[1] != "confused" && args[1] != "heart" && args[1] != "hooray" { + return &gomatrix.TextMessage{"m.notice", "Invalid reaction. Usage: " + cmdGithubReactUsage}, nil + } // get owner,repo,issue,resp out of args[0] owner, repo, issueNum, resp := s.getIssueDetailsFor(args[0], roomID, cmdGithubReactUsage) From 2b570d9b6c3dde234837c8d79eb33c527b83a753 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 22 Aug 2017 18:33:37 +0100 Subject: [PATCH 08/17] improve error from no default repo Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f4a968c..969bce0 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 @@ -305,7 +305,7 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s // issue only match, this only works if there is a default repo defaultRepo := s.defaultRepo(roomID) if defaultRepo == "" { - resp = &gomatrix.TextMessage{"m.notice", "Usage: " + usage} + resp = &gomatrix.TextMessage{"m.notice", "No default repo specified. Usage: " + usage} return } From c85abe84da8e21a17875d23f63cedfb684506296 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Aug 2017 16:34:02 +0100 Subject: [PATCH 09/17] update defaultRepo wording Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../go-neb/services/github/github.go | 70 ++++++++++++++++++- 1 file changed, 67 insertions(+), 3 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 969bce0..068db63 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 @@ -11,6 +11,7 @@ import ( "strconv" "strings" + "bytes" log "github.com/Sirupsen/logrus" gogithub "github.com/google/go-github/github" "github.com/matrix-org/go-neb/database" @@ -77,7 +78,64 @@ func (s *Service) requireGithubClientFor(userID string) (cli *gogithub.Client, r return } -const cmdGithubCreateUsage = `!github create owner/repo "issue title" "description"` +const numberGithubSearchSummaries = 3 +const cmdGithubSearchUsage = `!github create owner/repo "search query"` + +func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interface{}, error) { + cli, resp, err := s.requireGithubClientFor(userID) + if cli == nil { + return resp, err + } + if len(args) < 2 { + return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubSearchUsage}, nil + } + + query := fmt.Sprintf("repo:%s %s", args[0], strings.Join(args[1:], " ")) + searchResult, res, err := cli.Search.Issues(query, nil) + + if err != nil { + log.WithField("err", err).Print("Failed to react to issue") + if res == nil { + return nil, fmt.Errorf("Failed to react to issue. Failed to connect to Github") + } + return nil, fmt.Errorf("Failed to react to issue. HTTP %d", res.StatusCode) + } + + if searchResult.Total == nil || *searchResult.Total == 0 { + return &gomatrix.TextMessage{"m.notice", "No results found for your search query!"}, nil + } + + numResults := *searchResult.Total + var htmlBuffer bytes.Buffer + var plainBuffer bytes.Buffer + + numberOfSummaries := numberGithubSearchSummaries + if numResults < numberGithubSearchSummaries { + numberOfSummaries = numResults + } + + summarizedIssues := searchResult.Issues[0:numberOfSummaries] + + htmlBuffer.WriteString(fmt.Sprintf("Found %d results, here are the most relevant:
    ", numResults)) + plainBuffer.WriteString(fmt.Sprintf("Found %d results, here are the most relevant:\n", numResults)) + for i, issue := range summarizedIssues { + if issue.HTMLURL == nil || issue.User.Login == nil || issue.Title == nil { + continue + } + htmlBuffer.WriteString(fmt.Sprintf(`
  1. %s: %s
  2. `, *issue.HTMLURL, *issue.User.Login, *issue.Title)) + plainBuffer.WriteString(fmt.Sprintf("%d. %s\n", i+1, *issue.HTMLURL)) + } + htmlBuffer.WriteString("
") + + return &gomatrix.HTMLMessage{ + Body: plainBuffer.String(), + MsgType: "m.notice", + Format: "org.matrix.custom.html", + FormattedBody: htmlBuffer.String(), + }, nil +} + +const cmdGithubCreateUsage = `!github create [owner/repo] "issue title" "description"` func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) @@ -98,7 +156,7 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa // look for a default repo defaultRepo := s.defaultRepo(roomID) if defaultRepo == "" { - return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubCreateUsage}, nil + return &gomatrix.TextMessage{"m.notice", "Need to specify repo. Usage: " + cmdGithubCreateUsage}, nil } // default repo should pass the regexp ownerRepoGroups = ownerRepoRegex.FindStringSubmatch(defaultRepo) @@ -305,7 +363,7 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s // issue only match, this only works if there is a default repo defaultRepo := s.defaultRepo(roomID) if defaultRepo == "" { - resp = &gomatrix.TextMessage{"m.notice", "No default repo specified. Usage: " + usage} + resp = &gomatrix.TextMessage{"m.notice", "Need to specify repo. Usage: " + usage} return } @@ -351,6 +409,12 @@ func (s *Service) expandIssue(roomID, userID, owner, repo string, issueNum int) // is no link, it will return a Starter Link instead. func (s *Service) Commands(cli *gomatrix.Client) []types.Command { return []types.Command{ + types.Command{ + Path: []string{"github", "search"}, + Command: func(roomID, userID string, args []string) (interface{}, error) { + return s.cmdGithubSearch(roomID, userID, args) + }, + }, types.Command{ Path: []string{"github", "create"}, Command: func(roomID, userID string, args []string) (interface{}, error) { From c774bb893b665a11e4dd559d0188a7aada6cc224 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Aug 2017 16:37:47 +0100 Subject: [PATCH 10/17] update react cmd to use a string map for aliases Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../matrix-org/go-neb/services/github/github.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 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 068db63..5752f52 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 @@ -199,6 +199,15 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil } +var cmdGithubReactAliases = map[string]string{ + "+1": "+1", + "-1": "-1", + "laugh": "laugh", + "confused": "confused", + "heart": "heart", + "hooray": "hooray", +} + const cmdGithubReactUsage = `!github react [owner/repo]#issue (+1|-1|laugh|confused|heart|hooray)` func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interface{}, error) { @@ -209,7 +218,9 @@ func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interfac if len(args) < 2 { return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubReactUsage}, nil } - if args[1] != "+1" && args[1] != "-1" && args[1] != "laugh" && args[1] != "confused" && args[1] != "heart" && args[1] != "hooray" { + + reaction, ok := cmdGithubReactAliases[args[1]] + if !ok { return &gomatrix.TextMessage{"m.notice", "Invalid reaction. Usage: " + cmdGithubReactUsage}, nil } @@ -219,7 +230,7 @@ func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interfac return resp, nil } - _, res, err := cli.Reactions.CreateIssueReaction(owner, repo, issueNum, args[1]) + _, res, err := cli.Reactions.CreateIssueReaction(owner, repo, issueNum, reaction) if err != nil { log.WithField("err", err).Print("Failed to react to issue") From a440850ff1a4a876d579c41d9f487835bf3e4e00 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Aug 2017 16:44:56 +0100 Subject: [PATCH 11/17] add a bunch of reaction aliases Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../go-neb/services/github/github.go | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 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 5752f52..f99d0e0 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 @@ -200,12 +200,31 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa } var cmdGithubReactAliases = map[string]string{ - "+1": "+1", - "-1": "-1", - "laugh": "laugh", - "confused": "confused", - "heart": "heart", - "hooray": "hooray", + "+1": "+1", + ":+1:": "+1", + "👍": "+1", + + "-1": "-1", + ":-1:": "-1", + "👎": "-1", + + "laugh": "laugh", + "smile": "laugh", + ":smile:": "laugh", + "😄": "laugh", + + "confused": "confused", + ":confused:": "confused", + "😕": "confused", + + "heart": "heart", + ":heart:": "heart", + "❤": "heart", + + "hooray": "hooray", + "tada": "hooray", + ":tada:": "hooray", + "🎉": "hooray", } const cmdGithubReactUsage = `!github react [owner/repo]#issue (+1|-1|laugh|confused|heart|hooray)` From 67c121a4b0778cdef946e866deaff21895afe91d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Aug 2017 16:47:32 +0100 Subject: [PATCH 12/17] add github's weird padded heart emoji as alias Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 1 + 1 file changed, 1 insertion(+) 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 f99d0e0..20ba402 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 @@ -220,6 +220,7 @@ var cmdGithubReactAliases = map[string]string{ "heart": "heart", ":heart:": "heart", "❤": "heart", + "❤️": "heart", "hooray": "hooray", "tada": "hooray", From da576fa12d504f09c24336b76be81a4a3625fcfe Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 23 Aug 2017 16:51:14 +0100 Subject: [PATCH 13/17] add more aliases and throw them into the example in the usage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 20ba402..792c35f 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 @@ -212,10 +212,12 @@ var cmdGithubReactAliases = map[string]string{ "smile": "laugh", ":smile:": "laugh", "😄": "laugh", + "grin": "laugh", "confused": "confused", ":confused:": "confused", "😕": "confused", + "uncertain": "confused", "heart": "heart", ":heart:": "heart", @@ -228,7 +230,7 @@ var cmdGithubReactAliases = map[string]string{ "🎉": "hooray", } -const cmdGithubReactUsage = `!github react [owner/repo]#issue (+1|-1|laugh|confused|heart|hooray)` +const cmdGithubReactUsage = `!github react [owner/repo]#issue (+1|👍|-1|:-1:|laugh|:smile:|confused|uncertain|heart|❤|hooray|:tada:)` func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interface{}, error) { cli, resp, err := s.requireGithubClientFor(userID) From 2141a1a5dd943b42b3cb89b86b65a83aa901c2d1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 28 Aug 2017 18:00:30 +0100 Subject: [PATCH 14/17] fix error wording, allow github search without auth Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../matrix-org/go-neb/services/github/github.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 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 792c35f..cf94eef 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 @@ -82,10 +82,7 @@ const numberGithubSearchSummaries = 3 const cmdGithubSearchUsage = `!github create owner/repo "search query"` func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interface{}, error) { - cli, resp, err := s.requireGithubClientFor(userID) - if cli == nil { - return resp, err - } + cli := s.githubClientFor(userID, true) if len(args) < 2 { return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubSearchUsage}, nil } @@ -94,11 +91,11 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa searchResult, res, err := cli.Search.Issues(query, nil) if err != nil { - log.WithField("err", err).Print("Failed to react to issue") + log.WithField("err", err).Print("Failed to search") if res == nil { - return nil, fmt.Errorf("Failed to react to issue. Failed to connect to Github") + return nil, fmt.Errorf("Failed to search. Failed to connect to Github") } - return nil, fmt.Errorf("Failed to react to issue. HTTP %d", res.StatusCode) + return nil, fmt.Errorf("Failed to search. HTTP %d", res.StatusCode) } if searchResult.Total == nil || *searchResult.Total == 0 { @@ -122,7 +119,7 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa if issue.HTMLURL == nil || issue.User.Login == nil || issue.Title == nil { continue } - htmlBuffer.WriteString(fmt.Sprintf(`
  • %s: %s
  • `, *issue.HTMLURL, *issue.User.Login, *issue.Title)) + htmlBuffer.WriteString(fmt.Sprintf(`
  • %s: %s
  • `, *issue.HTMLURL, *issue.User.Login, *issue.Title)) plainBuffer.WriteString(fmt.Sprintf("%d. %s\n", i+1, *issue.HTMLURL)) } htmlBuffer.WriteString("") From 1cc6d0315f20a28765065d32f43d6e55369e8780 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 30 Aug 2017 11:26:16 +0100 Subject: [PATCH 15/17] move variable definitions closer to usage Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 5 ++--- 1 file changed, 2 insertions(+), 3 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 cf94eef..1491689 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 @@ -103,9 +103,6 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa } numResults := *searchResult.Total - var htmlBuffer bytes.Buffer - var plainBuffer bytes.Buffer - numberOfSummaries := numberGithubSearchSummaries if numResults < numberGithubSearchSummaries { numberOfSummaries = numResults @@ -113,6 +110,8 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa summarizedIssues := searchResult.Issues[0:numberOfSummaries] + var htmlBuffer bytes.Buffer + var plainBuffer bytes.Buffer htmlBuffer.WriteString(fmt.Sprintf("Found %d results, here are the most relevant:
      ", numResults)) plainBuffer.WriteString(fmt.Sprintf("Found %d results, here are the most relevant:\n", numResults)) for i, issue := range summarizedIssues { From c9eb00221e1f353ed9663025b68a15a0f6a7beb0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 30 Aug 2017 11:31:14 +0100 Subject: [PATCH 16/17] escape title and login, to prevent XSS Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- src/github.com/matrix-org/go-neb/services/github/github.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 1491689..5d55db5 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 @@ -20,6 +20,7 @@ import ( "github.com/matrix-org/go-neb/services/github/client" "github.com/matrix-org/go-neb/types" "github.com/matrix-org/gomatrix" + "html" ) // ServiceType of the Github service @@ -118,7 +119,8 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa if issue.HTMLURL == nil || issue.User.Login == nil || issue.Title == nil { continue } - htmlBuffer.WriteString(fmt.Sprintf(`
    1. %s: %s
    2. `, *issue.HTMLURL, *issue.User.Login, *issue.Title)) + escapedTitle, escapedUserLogin := html.EscapeString(*issue.Title), html.EscapeString(*issue.User.Login) + htmlBuffer.WriteString(fmt.Sprintf(`
    3. %s: %s
    4. `, *issue.HTMLURL, escapedUserLogin, escapedTitle)) plainBuffer.WriteString(fmt.Sprintf("%d. %s\n", i+1, *issue.HTMLURL)) } htmlBuffer.WriteString("
    ") From 64328cbb539b83765d28170e4930fa527e8418cf Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 30 Aug 2017 11:49:33 +0100 Subject: [PATCH 17/17] change loop to better define its behaviour Signed-off-by: Michael Telatynski <7t3chguy@gmail.com> --- .../matrix-org/go-neb/services/github/github.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 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 5d55db5..89398bb 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 @@ -104,18 +104,14 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa } numResults := *searchResult.Total - numberOfSummaries := numberGithubSearchSummaries - if numResults < numberGithubSearchSummaries { - numberOfSummaries = numResults - } - - summarizedIssues := searchResult.Issues[0:numberOfSummaries] - var htmlBuffer bytes.Buffer var plainBuffer bytes.Buffer htmlBuffer.WriteString(fmt.Sprintf("Found %d results, here are the most relevant:
      ", numResults)) plainBuffer.WriteString(fmt.Sprintf("Found %d results, here are the most relevant:\n", numResults)) - for i, issue := range summarizedIssues { + for i, issue := range searchResult.Issues { + if i >= numberGithubSearchSummaries { + break + } if issue.HTMLURL == nil || issue.User.Login == nil || issue.Title == nil { continue }