diff --git a/src/github.com/matrix-org/go-neb/matrix/types.go b/src/github.com/matrix-org/go-neb/matrix/types.go index 0db9054..900b16a 100644 --- a/src/github.com/matrix-org/go-neb/matrix/types.go +++ b/src/github.com/matrix-org/go-neb/matrix/types.go @@ -1,6 +1,7 @@ package matrix import ( + "encoding/json" "html" "regexp" ) @@ -114,3 +115,29 @@ func GetHTMLMessage(msgtype, htmlText string) HTMLMessage { FormattedBody: htmlText, } } + +// StarterLinkMessage represents a message with a starter_link custom data. +type StarterLinkMessage struct { + Body string + Link string +} + +// MarshalJSON converts this message into actual event content JSON. +func (m StarterLinkMessage) MarshalJSON() ([]byte, error) { + var data map[string]string + + if m.Link != "" { + data = map[string]string{ + "org.matrix.neb.starter_link": m.Link, + } + } + + msg := struct { + MsgType string `json:"msgtype"` + Body string `json:"body"` + Data map[string]string `json:"data,omitempty"` + }{ + "m.notice", m.Body, data, + } + return json.Marshal(msg) +} diff --git a/src/github.com/matrix-org/go-neb/realms/github/github.go b/src/github.com/matrix-org/go-neb/realms/github/github.go index 848ec1c..5ce18b1 100644 --- a/src/github.com/matrix-org/go-neb/realms/github/github.go +++ b/src/github.com/matrix-org/go-neb/realms/github/github.go @@ -12,11 +12,13 @@ import ( "net/url" ) -type githubRealm struct { +// GithubRealm can handle OAuth processes with github.com +type GithubRealm struct { id string redirectURL string ClientSecret string ClientID string + StarterLink string } // GithubSession represents an authenticated github session @@ -50,23 +52,28 @@ func (s *GithubSession) ID() string { return s.id } -func (r *githubRealm) ID() string { +// ID returns the realm ID +func (r *GithubRealm) ID() string { return r.id } -func (r *githubRealm) Type() string { +// Type is github +func (r *GithubRealm) Type() string { return "github" } -func (r *githubRealm) Init() error { +// Init does nothing. +func (r *GithubRealm) Init() error { return nil } -func (r *githubRealm) Register() error { +// Register does nothing. +func (r *GithubRealm) Register() error { return nil } -func (r *githubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} { +// RequestAuthSession generates an OAuth2 URL for this user to auth with github via. +func (r *GithubRealm) RequestAuthSession(userID string, req json.RawMessage) interface{} { state, err := randomString(10) if err != nil { log.WithError(err).Print("Failed to generate state param") @@ -95,7 +102,8 @@ func (r *githubRealm) RequestAuthSession(userID string, req json.RawMessage) int }{u.String()} } -func (r *githubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) { +// OnReceiveRedirect processes OAuth redirect requests from Github +func (r *GithubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) { // parse out params from the request code := req.URL.Query().Get("code") state := req.URL.Query().Get("state") @@ -153,7 +161,8 @@ func (r *githubRealm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request w.Write([]byte("OK!")) } -func (r *githubRealm) AuthSession(id, userID, realmID string) types.AuthSession { +// AuthSession returns a GithubSession for this user +func (r *GithubRealm) AuthSession(id, userID, realmID string) types.AuthSession { return &GithubSession{ id: id, userID: userID, @@ -180,6 +189,6 @@ func randomString(length int) (string, error) { func init() { types.RegisterAuthRealm(func(realmID, redirectURL string) types.AuthRealm { - return &githubRealm{id: realmID, redirectURL: redirectURL} + return &GithubRealm{id: realmID, redirectURL: redirectURL} }) } 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 f0b2bb6..e363c79 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 @@ -45,56 +45,95 @@ func (s *githubService) RoomIDs() []string { } return keys } + +func (s *githubService) cmdGithubCreate(roomID, userID string, args []string) (interface{}, error) { + cli := s.githubClientFor(userID, false) + if cli == nil { + r, err := database.GetServiceDB().LoadAuthRealm(s.RealmID) + if err != nil { + return nil, err + } + ghRealm, ok := r.(*realms.GithubRealm) + if !ok { + return nil, fmt.Errorf("Failed to cast realm %s into a GithubRealm", s.RealmID) + } + return matrix.StarterLinkMessage{ + Body: "You need to OAuth with Github before you can create issues.", + Link: ghRealm.StarterLink, + }, nil + } + + if len(args) < 2 { + return &matrix.TextMessage{"m.notice", + `Usage: !github create owner/repo "issue title" "description"`}, nil + } + + var ( + ownerRepo string + title *string + desc *string + ) + ownerRepo = args[0] + o := strings.Split(ownerRepo, "/") + if len(o) != 2 { + return &matrix.TextMessage{"m.notice", + `Usage: !github create owner/repo "issue title" "description"`}, nil + } + + if len(args) == 2 { + title = &args[1] + } else if len(args) == 3 { + title = &args[1] + desc = &args[2] + } else { // > 3 args is probably a title without quote marks + joinedTitle := strings.Join(args[1:], " ") + title = &joinedTitle + } + + issue, res, err := cli.Issues.Create(o[0], o[1], &github.IssueRequest{ + Title: title, + Body: desc, + }) + if err != nil { + log.WithField("err", err).Print("Failed to create issue") + return nil, fmt.Errorf("Failed to create issue. HTTP %d", res.StatusCode) + } + + return matrix.TextMessage{"m.notice", fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil +} + +func (s *githubService) expandIssue(roomID, userID, matchingText string) interface{} { + cli := s.githubClientFor(userID, true) + 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", + fmt.Sprintf("%s : %s", *i.HTMLURL, *i.Title), + } +} + func (s *githubService) Plugin(roomID string) plugin.Plugin { return plugin.Plugin{ Commands: []plugin.Command{ plugin.Command{ Path: []string{"github", "create"}, Command: func(roomID, userID string, args []string) (interface{}, error) { - cli := s.githubClientFor(userID, false) - if cli == nil { - // TODO: send starter link - return &matrix.TextMessage{"m.notice", - userID + " : You have not linked your Github account."}, nil - } - - if len(args) < 2 { - return &matrix.TextMessage{"m.notice", - `Usage: !github create owner/repo "issue title" "description"`}, nil - } - - var ( - ownerRepo string - title *string - desc *string - ) - ownerRepo = args[0] - o := strings.Split(ownerRepo, "/") - if len(o) != 2 { - return &matrix.TextMessage{"m.notice", - `Usage: !github create owner/repo "issue title" "description"`}, nil - } - - if len(args) == 2 { - title = &args[1] - } else if len(args) == 3 { - title = &args[1] - desc = &args[2] - } else { // > 3 args is probably a title without quote marks - joinedTitle := strings.Join(args[1:], " ") - title = &joinedTitle - } - - issue, res, err := cli.Issues.Create(o[0], o[1], &github.IssueRequest{ - Title: title, - Body: desc, - }) - if err != nil { - log.WithField("err", err).Print("Failed to create issue") - return nil, fmt.Errorf("Failed to create issue. HTTP %d", res.StatusCode) - } - - return matrix.TextMessage{"m.notice", fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil + return s.cmdGithubCreate(roomID, userID, args) }, }, }, @@ -102,28 +141,7 @@ func (s *githubService) Plugin(roomID string) plugin.Plugin { plugin.Expansion{ Regexp: ownerRepoIssueRegex, Expand: func(roomID, userID, matchingText string) interface{} { - cli := s.githubClientFor(userID, true) - 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", - fmt.Sprintf("%s : %s", *i.HTMLURL, *i.Title), - } + return s.expandIssue(roomID, userID, matchingText) }, }, },