Skip to content
This repository has been archived by the owner on Jun 28, 2023. It is now read-only.

Commit

Permalink
Merge pull request #358 from matrix-org/github-new_issue_labels
Browse files Browse the repository at this point in the history
Add support for GitHub labels when creating a new issue
  • Loading branch information
t3chguy authored Apr 13, 2022
2 parents 67a82fc + 4230799 commit 0260eac
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: '~1.16.4'
go-version: '~1.18.0'
- name: Install libolm
run: sudo apt-get -y install libolm3 libolm-dev
- name: Install linters
Expand Down
11 changes: 9 additions & 2 deletions clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -287,11 +287,12 @@ func (c *Clients) onBotOptionsEvent(client *mautrix.Client, event *mevt.Event) {
return
}
// these options fully clobber what was there previously.

opts := types.BotOptions{
UserID: client.UserID,
RoomID: event.RoomID,
SetByUserID: event.Sender,
Options: event.Content.Raw,
Options: event.Content.Parsed.(*types.BotOptionsContent),
}
if _, err := c.db.StoreBotOptions(opts); err != nil {
log.WithFields(log.Fields{
Expand Down Expand Up @@ -328,6 +329,8 @@ func (c *Clients) onRoomMemberEvent(client *mautrix.Client, event *mevt.Event) {
}
}

var StateBotOptionsEvent = mevt.Type{Type: "m.room.bot.options", Class: mevt.StateEventType}

func (c *Clients) initClient(botClient *BotClient) error {
config := botClient.config
client, err := mautrix.NewClient(config.HomeserverURL, config.UserID, config.AccessToken)
Expand All @@ -344,6 +347,10 @@ func (c *Clients) initClient(botClient *BotClient) error {
botClient.verificationSAS = &sync.Map{}

syncer := client.Syncer.(*mautrix.DefaultSyncer)
syncer.ParseEventContent = true

// Add m.room.bot.options to mautrix's TypeMap so that it parses it as a valid event
mevt.TypeMap[StateBotOptionsEvent] = reflect.TypeOf(types.BotOptionsContent{})

nebStore := &matrix.NEBStore{
InMemoryStore: *mautrix.NewInMemoryStore(),
Expand All @@ -366,7 +373,7 @@ func (c *Clients) initClient(botClient *BotClient) error {
c.onMessageEvent(botClient, event)
})

syncer.OnEventType(mevt.Type{Type: "m.room.bot.options", Class: mevt.UnknownEventType}, func(_ mautrix.EventSource, event *mevt.Event) {
syncer.OnEventType(StateBotOptionsEvent, func(_ mautrix.EventSource, event *mevt.Event) {
c.onBotOptionsEvent(botClient.Client, event)
})

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ require (
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
golang.org/x/net v0.0.0-20210220033124-5f55cee0dc0d
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.3.0
maunium.net/go/mautrix v0.9.12
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw=
golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
Expand Down
75 changes: 46 additions & 29 deletions services/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package github
import (
"context"
"database/sql"
"errors"
"fmt"
"regexp"
"strconv"
Expand Down Expand Up @@ -50,17 +51,20 @@ var ownerRepoRegex = regexp.MustCompile(`^([A-z0-9-_.]+)/([A-z0-9-_.]+)$`)
//
// Before you can set up a Github Service, you need to set up a Github Realm.
//
// You can set a "default repository" for a Matrix room by sending a `m.room.bot.options` state event
// You can set optional config for a Matrix room by sending a `m.room.bot.options` state event
// which has the following `content`:
//
// {
// "github": {
// "default_repo": "owner/repo"
// // The default repository to use for this room; this allows "owner/repo" to be omitted
// // when creating/expanding issues.
// "default_repo": "owner/repo",
//
// // Array of Github labels to attach to any issue created by this bot in this room.
// "new_issue_labels": ["bot-label-1", "bot-label-2"]
// }
// }
//
// This will allow the "owner/repo" to be omitted when creating/expanding issues.
//
// Example request:
// {
// "RealmID": "github-realm-id"
Expand Down Expand Up @@ -167,9 +171,18 @@ func (s *Service) cmdGithubCreate(roomID id.RoomID, userID id.UserID, args []str
// Look for a default if the first arg doesn't look like an owner/repo
ownerRepoGroups := ownerRepoRegex.FindStringSubmatch(args[0])

logger := log.WithFields(log.Fields{
"room_id": roomID,
"bot_user_id": s.ServiceUserID(),
})
options, err := s.loadBotOptions(roomID, logger)
if err != nil {
return nil, err
}

if len(ownerRepoGroups) == 0 {
// look for a default repo
defaultRepo := s.defaultRepo(roomID)
defaultRepo := options.DefaultRepo
if defaultRepo == "" {
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Expand Down Expand Up @@ -204,8 +217,9 @@ func (s *Service) cmdGithubCreate(roomID id.RoomID, userID id.UserID, args []str
}

issue, res, err := cli.Issues.Create(context.Background(), ownerRepoGroups[1], ownerRepoGroups[2], &gogithub.IssueRequest{
Title: title,
Body: desc,
Title: title,
Body: desc,
Labels: &options.NewIssueLabels,
})
if err != nil {
log.WithField("err", err).Print("Failed to create issue")
Expand Down Expand Up @@ -735,34 +749,37 @@ func (s *Service) Register(oldService types.Service, client types.MatrixClient)
return nil
}

func (s *Service) loadBotOptions(roomID id.RoomID, logger *log.Entry) (result types.GithubOptions, err error) {
opts, err := database.GetServiceDB().LoadBotOptions(s.ServiceUserID(), roomID)
if err != nil {
if err == sql.ErrNoRows {
logger.Info("no bot options specified - using defaults")
return types.GithubOptions{}, nil
} else {
err := errors.New("Failed to load bot options")
logger.WithError(err).Error(err)
return types.GithubOptions{}, err
}
}
// Expect opts to look like:
// {
// github: {
// default_repo: $OWNER_REPO,
// new_issue_labels: [ "label1", .. ]
// }
// }
return opts.Options.Github, nil
}

// defaultRepo returns the default repo for the given room, or an empty string.
func (s *Service) defaultRepo(roomID id.RoomID) string {
logger := log.WithFields(log.Fields{
"room_id": roomID,
"bot_user_id": s.ServiceUserID(),
})
opts, err := database.GetServiceDB().LoadBotOptions(s.ServiceUserID(), roomID)
if err != nil {
if err != sql.ErrNoRows {
logger.WithError(err).Error("Failed to load bot options")
}
return ""
}
// Expect opts to look like:
// { github: { default_repo: $OWNER_REPO } }
ghOpts, ok := opts.Options["github"].(map[string]interface{})
if !ok {
logger.WithField("options", opts.Options).Error("Failed to cast bot options as github options")
return ""
}
defaultRepo, ok := ghOpts["default_repo"].(string)
if !ok {
logger.WithField("default_repo", ghOpts["default_repo"]).Error(
"Failed to cast default repo as a string",
)
return ""
}
return defaultRepo
// ignore any errors, we treat it the same as no options and log inside the method
ghOpts, _ := s.loadBotOptions(roomID, logger)
return ghOpts.DefaultRepo
}

func (s *Service) githubClientFor(userID id.UserID, allowUnauth bool) *gogithub.Client {
Expand Down
11 changes: 10 additions & 1 deletion types/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,21 @@ import (
"maunium.net/go/mautrix/id"
)

type GithubOptions struct {
DefaultRepo string `json:"default_repo,omitempty"`
NewIssueLabels []string `json:"new_issue_labels,omitempty"`
}

type BotOptionsContent struct {
Github GithubOptions `json:"github"`
}

// BotOptions for a given bot user in a given room
type BotOptions struct {
RoomID id.RoomID
UserID id.UserID
SetByUserID id.UserID
Options map[string]interface{}
Options *BotOptionsContent
}

// Poller represents a thing which can poll. Services should implement this method signature to support polling.
Expand Down

0 comments on commit 0260eac

Please sign in to comment.