Browse Source

Convert and re-enable Github realm and service, and update go-github

version

Signed-off-by: Nikos Filippakis <me@nfil.dev>
pull/322/head
Nikos Filippakis 5 years ago
parent
commit
69d063f0ec
  1. 2
      go.mod
  2. 6
      goneb.go
  3. 14
      realms/github/github.go
  4. 215
      services/github/github.go
  5. 29
      services/github/github_webhook.go
  6. 9
      services/github/github_webhook_test.go
  7. 7
      services/github/webhook/webhook.go
  8. 21
      services/utils/utils.go
  9. 19
      services/utils/utils_test.go

2
go.mod

@ -17,7 +17,7 @@ require (
github.com/gogo/protobuf v1.1.1 // indirect github.com/gogo/protobuf v1.1.1 // indirect
github.com/golang/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.3.2 // indirect
github.com/google/go-cmp v0.4.0 // indirect github.com/google/go-cmp v0.4.0 // indirect
github.com/google/go-github v2.0.1-0.20160719063544-b5e5babef39c+incompatible
github.com/google/go-github v17.0.0+incompatible
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195 github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195
github.com/json-iterator/go v1.1.9 // indirect github.com/json-iterator/go v1.1.9 // indirect

6
goneb.go

@ -17,14 +17,14 @@ import (
"github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/database"
_ "github.com/matrix-org/go-neb/metrics" _ "github.com/matrix-org/go-neb/metrics"
"github.com/matrix-org/go-neb/polling" "github.com/matrix-org/go-neb/polling"
//_ "github.com/matrix-org/go-neb/realms/github"
_ "github.com/matrix-org/go-neb/realms/github"
_ "github.com/matrix-org/go-neb/realms/jira" _ "github.com/matrix-org/go-neb/realms/jira"
//_ "github.com/matrix-org/go-neb/services/alertmanager" //_ "github.com/matrix-org/go-neb/services/alertmanager"
_ "github.com/matrix-org/go-neb/services/echo" _ "github.com/matrix-org/go-neb/services/echo"
_ "github.com/matrix-org/go-neb/services/giphy" _ "github.com/matrix-org/go-neb/services/giphy"
_ "github.com/matrix-org/go-neb/services/github"
//_ "github.com/matrix-org/go-neb/services/github"
//_ "github.com/matrix-org/go-neb/services/google" //_ "github.com/matrix-org/go-neb/services/google"
_ "github.com/matrix-org/go-neb/services/guggy" _ "github.com/matrix-org/go-neb/services/guggy"
_ "github.com/matrix-org/go-neb/services/imgur" _ "github.com/matrix-org/go-neb/services/imgur"

14
realms/github/github.go

@ -2,6 +2,7 @@
package github package github
import ( import (
"context"
"crypto/rand" "crypto/rand"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
@ -14,6 +15,7 @@ import (
"github.com/matrix-org/go-neb/services/github/client" "github.com/matrix-org/go-neb/services/github/client"
"github.com/matrix-org/go-neb/types" "github.com/matrix-org/go-neb/types"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix/id"
) )
// RealmType of the Github Realm // RealmType of the Github Realm
@ -41,7 +43,7 @@ type Realm struct {
// Session represents an authenticated github session // Session represents an authenticated github session
type Session struct { type Session struct {
id string id string
userID string
userID id.UserID
realmID string realmID string
// AccessToken is the github access token for the user // AccessToken is the github access token for the user
@ -86,7 +88,7 @@ func (s *Session) Info() interface{} {
} }
for { for {
// query for a list of possible projects // query for a list of possible projects
rs, resp, err := cli.Repositories.List("", opts)
rs, resp, err := cli.Repositories.List(context.Background(), "", opts)
if err != nil { if err != nil {
logger.WithError(err).Print("Failed to query github projects on github.com") logger.WithError(err).Print("Failed to query github projects on github.com")
return nil return nil
@ -110,7 +112,7 @@ func (s *Session) Info() interface{} {
} }
// UserID returns the user_id who authorised with Github // UserID returns the user_id who authorised with Github
func (s *Session) UserID() string {
func (s *Session) UserID() id.UserID {
return s.userID return s.userID
} }
@ -156,7 +158,7 @@ func (r *Realm) Register() error {
// { // {
// "URL": "https://github.com/login/oauth/authorize?client_id=abcdef&client_secret=acascacac...." // "URL": "https://github.com/login/oauth/authorize?client_id=abcdef&client_secret=acascacac...."
// } // }
func (r *Realm) RequestAuthSession(userID string, req json.RawMessage) interface{} {
func (r *Realm) RequestAuthSession(userID id.UserID, req json.RawMessage) interface{} {
state, err := randomString(10) state, err := randomString(10)
if err != nil { if err != nil {
log.WithError(err).Print("Failed to generate state param") log.WithError(err).Print("Failed to generate state param")
@ -259,7 +261,7 @@ func (r *Realm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {
return return
} }
r.redirectOr( r.redirectOr(
w, 200, "You have successfully linked your Github account to "+ghSession.UserID(), logger, ghSession,
w, 200, "You have successfully linked your Github account to "+ghSession.UserID().String(), logger, ghSession,
) )
} }
@ -275,7 +277,7 @@ func (r *Realm) redirectOr(w http.ResponseWriter, code int, msg string, logger *
} }
// AuthSession returns a Github Session for this user // AuthSession returns a Github Session for this user
func (r *Realm) AuthSession(id, userID, realmID string) types.AuthSession {
func (r *Realm) AuthSession(id string, userID id.UserID, realmID string) types.AuthSession {
return &Session{ return &Session{
id: id, id: id,
userID: userID, userID: userID,

215
services/github/github.go

@ -5,6 +5,7 @@
package github package github
import ( import (
"context"
"database/sql" "database/sql"
"fmt" "fmt"
"regexp" "regexp"
@ -12,15 +13,18 @@ import (
"strings" "strings"
"bytes" "bytes"
"html"
gogithub "github.com/google/go-github/github" gogithub "github.com/google/go-github/github"
"github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/matrix" "github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/realms/github" "github.com/matrix-org/go-neb/realms/github"
"github.com/matrix-org/go-neb/services/github/client" "github.com/matrix-org/go-neb/services/github/client"
"github.com/matrix-org/go-neb/types" "github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"html"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
) )
// ServiceType of the Github service // ServiceType of the Github service
@ -69,7 +73,7 @@ type Service struct {
RealmID string RealmID string
} }
func (s *Service) requireGithubClientFor(userID string) (cli *gogithub.Client, resp interface{}, err error) {
func (s *Service) requireGithubClientFor(userID id.UserID) (cli *gogithub.Client, resp interface{}, err error) {
cli = s.githubClientFor(userID, false) cli = s.githubClientFor(userID, false)
if cli == nil { if cli == nil {
var r types.AuthRealm var r types.AuthRealm
@ -91,14 +95,17 @@ func (s *Service) requireGithubClientFor(userID string) (cli *gogithub.Client, r
const numberGithubSearchSummaries = 3 const numberGithubSearchSummaries = 3
const cmdGithubSearchUsage = `!github search "search query"` const cmdGithubSearchUsage = `!github search "search query"`
func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGithubSearch(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
cli := s.githubClientFor(userID, true) cli := s.githubClientFor(userID, true)
if len(args) < 2 { if len(args) < 2 {
return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubSearchUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: " + cmdGithubSearchUsage,
}, nil
} }
query := strings.Join(args, " ") query := strings.Join(args, " ")
searchResult, res, err := cli.Search.Issues(query, nil)
searchResult, res, err := cli.Search.Issues(context.Background(), query, nil)
if err != nil { if err != nil {
log.WithField("err", err).Print("Failed to search") log.WithField("err", err).Print("Failed to search")
@ -109,7 +116,10 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa
} }
if searchResult.Total == nil || *searchResult.Total == 0 { if searchResult.Total == nil || *searchResult.Total == 0 {
return &gomatrix.TextMessage{"m.notice", "No results found for your search query!"}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "No results found for your search query!",
}, nil
} }
numResults := *searchResult.Total numResults := *searchResult.Total
@ -130,9 +140,9 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa
} }
htmlBuffer.WriteString("</ol>") htmlBuffer.WriteString("</ol>")
return &gomatrix.HTMLMessage{
return &mevt.MessageEventContent{
Body: plainBuffer.String(), Body: plainBuffer.String(),
MsgType: "m.notice",
MsgType: mevt.MsgNotice,
Format: "org.matrix.custom.html", Format: "org.matrix.custom.html",
FormattedBody: htmlBuffer.String(), FormattedBody: htmlBuffer.String(),
}, nil }, nil
@ -140,13 +150,16 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa
const cmdGithubCreateUsage = `!github create [owner/repo] "issue title" "description"` const cmdGithubCreateUsage = `!github create [owner/repo] "issue title" "description"`
func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGithubCreate(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
cli, resp, err := s.requireGithubClientFor(userID) cli, resp, err := s.requireGithubClientFor(userID)
if cli == nil { if cli == nil {
return resp, err return resp, err
} }
if len(args) == 0 { if len(args) == 0 {
return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubCreateUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: " + cmdGithubCreateUsage,
}, nil
} }
// We expect the args to look like: // We expect the args to look like:
@ -159,12 +172,16 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa
// look for a default repo // look for a default repo
defaultRepo := s.defaultRepo(roomID) defaultRepo := s.defaultRepo(roomID)
if defaultRepo == "" { if defaultRepo == "" {
return &gomatrix.TextMessage{"m.notice", "Need to specify repo. Usage: " + cmdGithubCreateUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Need to specify repo. Usage: " + cmdGithubCreateUsage,
}, nil
} }
// default repo should pass the regexp // default repo should pass the regexp
ownerRepoGroups = ownerRepoRegex.FindStringSubmatch(defaultRepo) ownerRepoGroups = ownerRepoRegex.FindStringSubmatch(defaultRepo)
if len(ownerRepoGroups) == 0 { if len(ownerRepoGroups) == 0 {
return &gomatrix.TextMessage{"m.notice", "Malformed default repo. Usage: " + cmdGithubCreateUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice, Body: "Malformed default repo. Usage: " + cmdGithubCreateUsage}, nil
} }
// insert the default as the first arg to reuse the same indices // insert the default as the first arg to reuse the same indices
@ -187,7 +204,7 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa
title = &joinedTitle title = &joinedTitle
} }
issue, res, err := cli.Issues.Create(ownerRepoGroups[1], ownerRepoGroups[2], &gogithub.IssueRequest{
issue, res, err := cli.Issues.Create(context.Background(), ownerRepoGroups[1], ownerRepoGroups[2], &gogithub.IssueRequest{
Title: title, Title: title,
Body: desc, Body: desc,
}) })
@ -199,7 +216,8 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa
return nil, fmt.Errorf("Failed to create issue. HTTP %d", res.StatusCode) return nil, fmt.Errorf("Failed to create issue. HTTP %d", res.StatusCode)
} }
return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice, Body: fmt.Sprintf("Created issue: %s", *issue.HTMLURL)}, nil
} }
var cmdGithubReactAliases = map[string]string{ var cmdGithubReactAliases = map[string]string{
@ -235,18 +253,23 @@ var cmdGithubReactAliases = map[string]string{
const cmdGithubReactUsage = `!github react [owner/repo]#issue (+1|👍|-1|:-1:|laugh|:smile:|confused|uncertain|heart|❤|hooray|:tada:)` 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) {
func (s *Service) cmdGithubReact(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
cli, resp, err := s.requireGithubClientFor(userID) cli, resp, err := s.requireGithubClientFor(userID)
if cli == nil { if cli == nil {
return resp, err return resp, err
} }
if len(args) < 2 { if len(args) < 2 {
return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubReactUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice, Body: "Usage: " + cmdGithubReactUsage,
}, nil
} }
reaction, ok := cmdGithubReactAliases[args[1]] reaction, ok := cmdGithubReactAliases[args[1]]
if !ok { if !ok {
return &gomatrix.TextMessage{"m.notice", "Invalid reaction. Usage: " + cmdGithubReactUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Invalid reaction. Usage: " + cmdGithubReactUsage,
}, nil
} }
// get owner,repo,issue,resp out of args[0] // get owner,repo,issue,resp out of args[0]
@ -255,7 +278,7 @@ func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interfac
return resp, nil return resp, nil
} }
_, res, err := cli.Reactions.CreateIssueReaction(owner, repo, issueNum, reaction)
_, res, err := cli.Reactions.CreateIssueReaction(context.Background(), owner, repo, issueNum, reaction)
if err != nil { if err != nil {
log.WithField("err", err).Print("Failed to react to issue") log.WithField("err", err).Print("Failed to react to issue")
@ -265,18 +288,24 @@ func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interfac
return nil, fmt.Errorf("Failed to react to issue. HTTP %d", res.StatusCode) 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
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: fmt.Sprintf("Reacted to issue with: %s", args[1]),
}, nil
} }
const cmdGithubCommentUsage = `!github comment [owner/repo]#issue "comment text"` const cmdGithubCommentUsage = `!github comment [owner/repo]#issue "comment text"`
func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGithubComment(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
cli, resp, err := s.requireGithubClientFor(userID) cli, resp, err := s.requireGithubClientFor(userID)
if cli == nil { if cli == nil {
return resp, err return resp, err
} }
if len(args) == 0 { if len(args) == 0 {
return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubCommentUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: " + cmdGithubCommentUsage,
}, nil
} }
// get owner,repo,issue,resp out of args[0] // get owner,repo,issue,resp out of args[0]
@ -294,7 +323,7 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf
comment = &joinedComment comment = &joinedComment
} }
issueComment, res, err := cli.Issues.CreateComment(owner, repo, issueNum, &gogithub.IssueComment{
issueComment, res, err := cli.Issues.CreateComment(context.Background(), owner, repo, issueNum, &gogithub.IssueComment{
Body: comment, Body: comment,
}) })
@ -306,20 +335,29 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf
return nil, fmt.Errorf("Failed to create issue comment. 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
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: fmt.Sprintf("Commented on issue: %s", *issueComment.HTMLURL),
}, nil
} }
const cmdGithubAssignUsage = `!github assign [owner/repo]#issue username [username] [...]` const cmdGithubAssignUsage = `!github assign [owner/repo]#issue username [username] [...]`
func (s *Service) cmdGithubAssign(roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGithubAssign(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
cli, resp, err := s.requireGithubClientFor(userID) cli, resp, err := s.requireGithubClientFor(userID)
if cli == nil { if cli == nil {
return resp, err return resp, err
} }
if len(args) < 1 { if len(args) < 1 {
return &gomatrix.TextMessage{"m.notice", "Usage: " + cmdGithubAssignUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: " + cmdGithubAssignUsage,
}, nil
} else if len(args) < 2 { } else if len(args) < 2 {
return &gomatrix.TextMessage{"m.notice", "Needs at least one username. Usage: " + cmdGithubAssignUsage}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Needs at least one username. Usage: " + cmdGithubAssignUsage,
}, nil
} }
// get owner,repo,issue,resp out of args[0] // get owner,repo,issue,resp out of args[0]
@ -328,7 +366,7 @@ func (s *Service) cmdGithubAssign(roomID, userID string, args []string) (interfa
return resp, nil return resp, nil
} }
issue, res, err := cli.Issues.AddAssignees(owner, repo, issueNum, args[1:])
issue, res, err := cli.Issues.AddAssignees(context.Background(), owner, repo, issueNum, args[1:])
if err != nil { if err != nil {
log.WithField("err", err).Print("Failed to add issue assignees") log.WithField("err", err).Print("Failed to add issue assignees")
@ -338,16 +376,22 @@ func (s *Service) cmdGithubAssign(roomID, userID string, args []string) (interfa
return nil, fmt.Errorf("Failed to add issue assignees. HTTP %d", res.StatusCode) 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
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: fmt.Sprintf("Added assignees to issue: %s", *issue.HTMLURL),
}, nil
} }
func (s *Service) githubIssueCloseReopen(roomID, userID string, args []string, state, verb, help string) (interface{}, error) {
func (s *Service) githubIssueCloseReopen(roomID id.RoomID, userID id.UserID, args []string, state, verb, help string) (interface{}, error) {
cli, resp, err := s.requireGithubClientFor(userID) cli, resp, err := s.requireGithubClientFor(userID)
if cli == nil { if cli == nil {
return resp, err return resp, err
} }
if len(args) == 0 { if len(args) == 0 {
return &gomatrix.TextMessage{"m.notice", "Usage: " + help}, nil
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: " + help,
}, nil
} }
// get owner,repo,issue,resp out of args[0] // get owner,repo,issue,resp out of args[0]
@ -356,7 +400,7 @@ func (s *Service) githubIssueCloseReopen(roomID, userID string, args []string, s
return resp, nil return resp, nil
} }
issueComment, res, err := cli.Issues.Edit(owner, repo, issueNum, &gogithub.IssueRequest{
issueComment, res, err := cli.Issues.Edit(context.Background(), owner, repo, issueNum, &gogithub.IssueRequest{
State: &state, State: &state,
}) })
@ -368,22 +412,25 @@ func (s *Service) githubIssueCloseReopen(roomID, userID string, args []string, s
return nil, fmt.Errorf("Failed to %s issue. HTTP %d", verb, res.StatusCode) return nil, fmt.Errorf("Failed to %s issue. HTTP %d", verb, res.StatusCode)
} }
return gomatrix.TextMessage{"m.notice", fmt.Sprintf("Closed issue: %s", *issueComment.HTMLURL)}, nil
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: fmt.Sprintf("Closed issue: %s", *issueComment.HTMLURL),
}, nil
} }
const cmdGithubCloseUsage = `!github close [owner/repo]#issue` const cmdGithubCloseUsage = `!github close [owner/repo]#issue`
func (s *Service) cmdGithubClose(roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGithubClose(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.githubIssueCloseReopen(roomID, userID, args, "closed", "close", cmdGithubCloseUsage) return s.githubIssueCloseReopen(roomID, userID, args, "closed", "close", cmdGithubCloseUsage)
} }
const cmdGithubReopenUsage = `!github reopen [owner/repo]#issue` const cmdGithubReopenUsage = `!github reopen [owner/repo]#issue`
func (s *Service) cmdGithubReopen(roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGithubReopen(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.githubIssueCloseReopen(roomID, userID, args, "open", "open", cmdGithubCloseUsage) return s.githubIssueCloseReopen(roomID, userID, args, "open", "open", cmdGithubCloseUsage)
} }
func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo string, issueNum int, resp interface{}) {
func (s *Service) getIssueDetailsFor(input string, roomID id.RoomID, usage string) (owner, repo string, issueNum int, resp interface{}) {
// We expect the input to look like: // We expect the input to look like:
// "[owner/repo]#issue" // "[owner/repo]#issue"
// They can omit the owner/repo if there is a default one set. // They can omit the owner/repo if there is a default one set.
@ -391,7 +438,10 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s
ownerRepoIssueGroups := ownerRepoIssueRegexAnchored.FindStringSubmatch(input) ownerRepoIssueGroups := ownerRepoIssueRegexAnchored.FindStringSubmatch(input)
if len(ownerRepoIssueGroups) != 5 { if len(ownerRepoIssueGroups) != 5 {
resp = &gomatrix.TextMessage{"m.notice", "Usage: " + usage}
resp = &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: " + usage,
}
return return
} }
@ -400,7 +450,10 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s
var err error var err error
if issueNum, err = strconv.Atoi(ownerRepoIssueGroups[4]); err != nil { if issueNum, err = strconv.Atoi(ownerRepoIssueGroups[4]); err != nil {
resp = &gomatrix.TextMessage{"m.notice", "Malformed issue number. Usage: " + usage}
resp = &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Malformed issue number. Usage: " + usage,
}
return return
} }
@ -408,13 +461,19 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s
// issue only match, this only works if there is a default repo // issue only match, this only works if there is a default repo
defaultRepo := s.defaultRepo(roomID) defaultRepo := s.defaultRepo(roomID)
if defaultRepo == "" { if defaultRepo == "" {
resp = &gomatrix.TextMessage{"m.notice", "Need to specify repo. Usage: " + usage}
resp = &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Need to specify repo. Usage: " + usage,
}
return return
} }
segs := strings.Split(defaultRepo, "/") segs := strings.Split(defaultRepo, "/")
if len(segs) != 2 { if len(segs) != 2 {
resp = &gomatrix.TextMessage{"m.notice", "Malformed default repo. Usage: " + usage}
resp = &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Malformed default repo. Usage: " + usage,
}
return return
} }
@ -424,10 +483,10 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s
return return
} }
func (s *Service) expandIssue(roomID, userID, owner, repo string, issueNum int) interface{} {
func (s *Service) expandIssue(roomID id.RoomID, userID id.UserID, owner, repo string, issueNum int) interface{} {
cli := s.githubClientFor(userID, true) cli := s.githubClientFor(userID, true)
i, _, err := cli.Issues.Get(owner, repo, issueNum)
i, _, err := cli.Issues.Get(context.Background(), owner, repo, issueNum)
if err != nil { if err != nil {
log.WithError(err).WithFields(log.Fields{ log.WithError(err).WithFields(log.Fields{
"owner": owner, "owner": owner,
@ -437,16 +496,16 @@ func (s *Service) expandIssue(roomID, userID, owner, repo string, issueNum int)
return nil return nil
} }
return &gomatrix.TextMessage{
"m.notice",
fmt.Sprintf("%s : %s", *i.HTMLURL, *i.Title),
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: fmt.Sprintf("%s : %s", *i.HTMLURL, *i.Title),
} }
} }
func (s *Service) expandCommit(roomID, userID, owner, repo, sha string) interface{} {
func (s *Service) expandCommit(roomID id.RoomID, userID id.UserID, owner, repo, sha string) interface{} {
cli := s.githubClientFor(userID, true) cli := s.githubClientFor(userID, true)
c, _, err := cli.Repositories.GetCommit(owner, repo, sha)
c, _, err := cli.Repositories.GetCommit(context.Background(), owner, repo, sha)
if err != nil { if err != nil {
log.WithError(err).WithFields(log.Fields{ log.WithError(err).WithFields(log.Fields{
"owner": owner, "owner": owner,
@ -487,10 +546,10 @@ func (s *Service) expandCommit(roomID, userID, owner, repo, sha string) interfac
plainBuffer.WriteString(segs[0]) plainBuffer.WriteString(segs[0])
} }
return &gomatrix.HTMLMessage{
return &mevt.MessageEventContent{
Body: plainBuffer.String(), Body: plainBuffer.String(),
MsgType: "m.notice",
Format: "org.matrix.custom.html",
MsgType: mevt.MsgNotice,
Format: mevt.FormatHTML,
FormattedBody: htmlBuffer.String(), FormattedBody: htmlBuffer.String(),
} }
} }
@ -504,56 +563,56 @@ func (s *Service) expandCommit(roomID, userID, owner, repo, sha string) interfac
// Responds with the outcome of the issue comment creation request. This command requires // Responds with the outcome of the issue comment creation request. This command requires
// a Github account to be linked to the Matrix user ID issuing the command. If there // a Github account to be linked to the Matrix user ID issuing the command. If there
// is no link, it will return a Starter Link instead. // is no link, it will return a Starter Link instead.
func (s *Service) Commands(cli *gomatrix.Client) []types.Command {
func (s *Service) Commands(cli *mautrix.Client) []types.Command {
return []types.Command{ return []types.Command{
types.Command{
{
Path: []string{"github", "search"}, Path: []string{"github", "search"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGithubSearch(roomID, userID, args) return s.cmdGithubSearch(roomID, userID, args)
}, },
}, },
types.Command{
{
Path: []string{"github", "create"}, Path: []string{"github", "create"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGithubCreate(roomID, userID, args) return s.cmdGithubCreate(roomID, userID, args)
}, },
}, },
types.Command{
{
Path: []string{"github", "react"}, Path: []string{"github", "react"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGithubReact(roomID, userID, args) return s.cmdGithubReact(roomID, userID, args)
}, },
}, },
types.Command{
{
Path: []string{"github", "comment"}, Path: []string{"github", "comment"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGithubComment(roomID, userID, args) return s.cmdGithubComment(roomID, userID, args)
}, },
}, },
types.Command{
{
Path: []string{"github", "assign"}, Path: []string{"github", "assign"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGithubAssign(roomID, userID, args) return s.cmdGithubAssign(roomID, userID, args)
}, },
}, },
types.Command{
{
Path: []string{"github", "close"}, Path: []string{"github", "close"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGithubClose(roomID, userID, args) return s.cmdGithubClose(roomID, userID, args)
}, },
}, },
types.Command{
{
Path: []string{"github", "reopen"}, Path: []string{"github", "reopen"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGithubReopen(roomID, userID, args) return s.cmdGithubReopen(roomID, userID, args)
}, },
}, },
types.Command{
{
Path: []string{"github", "help"}, Path: []string{"github", "help"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
return &gomatrix.TextMessage{
"m.notice",
strings.Join([]string{
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: strings.Join([]string{
cmdGithubCreateUsage, cmdGithubCreateUsage,
cmdGithubReactUsage, cmdGithubReactUsage,
cmdGithubCommentUsage, cmdGithubCommentUsage,
@ -573,11 +632,11 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command {
// it will also expand strings of the form: // it will also expand strings of the form:
// #12 // #12
// using the default repository. // using the default repository.
func (s *Service) Expansions(cli *gomatrix.Client) []types.Expansion {
func (s *Service) Expansions(cli *mautrix.Client) []types.Expansion {
return []types.Expansion{ return []types.Expansion{
types.Expansion{ types.Expansion{
Regexp: ownerRepoIssueRegex, Regexp: ownerRepoIssueRegex,
Expand: func(roomID, userID string, matchingGroups []string) interface{} {
Expand: func(roomID id.RoomID, userID id.UserID, matchingGroups []string) interface{} {
// There's an optional group in the regex so matchingGroups can look like: // There's an optional group in the regex so matchingGroups can look like:
// [foo/bar#55 foo bar 55] // [foo/bar#55 foo bar 55]
// [#55 55] // [#55 55]
@ -619,7 +678,7 @@ func (s *Service) Expansions(cli *gomatrix.Client) []types.Expansion {
}, },
types.Expansion{ types.Expansion{
Regexp: ownerRepoCommitRegex, Regexp: ownerRepoCommitRegex,
Expand: func(roomID, userID string, matchingGroups []string) interface{} {
Expand: func(roomID id.RoomID, userID id.UserID, matchingGroups []string) interface{} {
// There's an optional group in the regex so matchingGroups can look like: // There's an optional group in the regex so matchingGroups can look like:
// [foo/bar@a123 foo bar a123] // [foo/bar@a123 foo bar a123]
// [@a123 a123] // [@a123 a123]
@ -659,7 +718,7 @@ func (s *Service) Expansions(cli *gomatrix.Client) []types.Expansion {
} }
// Register makes sure that the given realm ID maps to a github realm. // Register makes sure that the given realm ID maps to a github realm.
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error {
func (s *Service) Register(oldService types.Service, client *mautrix.Client) error {
if s.RealmID == "" { if s.RealmID == "" {
return fmt.Errorf("RealmID is required") return fmt.Errorf("RealmID is required")
} }
@ -678,7 +737,7 @@ func (s *Service) Register(oldService types.Service, client *gomatrix.Client) er
} }
// defaultRepo returns the default repo for the given room, or an empty string. // defaultRepo returns the default repo for the given room, or an empty string.
func (s *Service) defaultRepo(roomID string) string {
func (s *Service) defaultRepo(roomID id.RoomID) string {
logger := log.WithFields(log.Fields{ logger := log.WithFields(log.Fields{
"room_id": roomID, "room_id": roomID,
"bot_user_id": s.ServiceUserID(), "bot_user_id": s.ServiceUserID(),
@ -707,7 +766,7 @@ func (s *Service) defaultRepo(roomID string) string {
return defaultRepo return defaultRepo
} }
func (s *Service) githubClientFor(userID string, allowUnauth bool) *gogithub.Client {
func (s *Service) githubClientFor(userID id.UserID, allowUnauth bool) *gogithub.Client {
token, err := getTokenForUser(s.RealmID, userID) token, err := getTokenForUser(s.RealmID, userID)
if err != nil { if err != nil {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
@ -725,7 +784,7 @@ func (s *Service) githubClientFor(userID string, allowUnauth bool) *gogithub.Cli
} }
} }
func getTokenForUser(realmID, userID string) (string, error) {
func getTokenForUser(realmID string, userID id.UserID) (string, error) {
realm, err := database.GetServiceDB().LoadAuthRealm(realmID) realm, err := database.GetServiceDB().LoadAuthRealm(realmID)
if err != nil { if err != nil {
return "", err return "", err
@ -750,7 +809,7 @@ func getTokenForUser(realmID, userID string) (string, error) {
} }
func init() { func init() {
types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
types.RegisterService(func(serviceID string, serviceUserID id.UserID, webhookEndpointURL string) types.Service {
return &Service{ return &Service{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType), DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
} }

29
services/github/github_webhook.go

@ -1,6 +1,7 @@
package github package github
import ( import (
"context"
"fmt" "fmt"
"net/http" "net/http"
"sort" "sort"
@ -11,8 +12,10 @@ import (
"github.com/matrix-org/go-neb/services/github/client" "github.com/matrix-org/go-neb/services/github/client"
"github.com/matrix-org/go-neb/services/github/webhook" "github.com/matrix-org/go-neb/services/github/webhook"
"github.com/matrix-org/go-neb/types" "github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
) )
// WebhookServiceType of the Github Webhook service. // WebhookServiceType of the Github Webhook service.
@ -45,12 +48,12 @@ type WebhookService struct {
types.DefaultService types.DefaultService
webhookEndpointURL string webhookEndpointURL string
// The user ID to create/delete webhooks as. // The user ID to create/delete webhooks as.
ClientUserID string
ClientUserID id.UserID
// The ID of an existing "github" realm. This realm will be used to obtain // The ID of an existing "github" realm. This realm will be used to obtain
// the Github credentials of the ClientUserID. // the Github credentials of the ClientUserID.
RealmID string RealmID string
// A map from Matrix room ID to Github "owner/repo"-style repositories. // A map from Matrix room ID to Github "owner/repo"-style repositories.
Rooms map[string]struct {
Rooms map[id.RoomID]struct {
// A map of "owner/repo"-style repositories to the events to listen for. // A map of "owner/repo"-style repositories to the events to listen for.
Repos map[string]struct { // owner/repo => { events: ["push","issue","pull_request"] } Repos map[string]struct { // owner/repo => { events: ["push","issue","pull_request"] }
// The webhook events to listen for. Currently supported: // The webhook events to listen for. Currently supported:
@ -79,7 +82,7 @@ type WebhookService struct {
// //
// If the "owner/repo" string doesn't exist in this Service config, then the webhook will be deleted from // If the "owner/repo" string doesn't exist in this Service config, then the webhook will be deleted from
// Github. // Github.
func (s *WebhookService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) {
func (s *WebhookService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *mautrix.Client) {
evType, repo, msg, err := webhook.OnReceiveRequest(req, s.SecretToken) evType, repo, msg, err := webhook.OnReceiveRequest(req, s.SecretToken)
if err != nil { if err != nil {
w.WriteHeader(err.Code) w.WriteHeader(err.Code)
@ -108,7 +111,7 @@ func (s *WebhookService) OnReceiveWebhook(w http.ResponseWriter, req *http.Reque
"message": msg, "message": msg,
"room_id": roomID, "room_id": roomID,
}).Print("Sending notification to room") }).Print("Sending notification to room")
if _, e := cli.SendMessageEvent(roomID, "m.room.message", msg); e != nil {
if _, e := cli.SendMessageEvent(roomID, event.EventMessage, msg); e != nil {
logger.WithError(e).WithField("room_id", roomID).Print( logger.WithError(e).WithField("room_id", roomID).Print(
"Failed to send notification to room.") "Failed to send notification to room.")
} }
@ -143,7 +146,7 @@ func (s *WebhookService) OnReceiveWebhook(w http.ResponseWriter, req *http.Reque
// //
// Hooks can get out of sync if a user manually deletes a hook in the Github UI. In this case, toggling the repo configuration will // Hooks can get out of sync if a user manually deletes a hook in the Github UI. In this case, toggling the repo configuration will
// force NEB to recreate the hook. // force NEB to recreate the hook.
func (s *WebhookService) Register(oldService types.Service, client *gomatrix.Client) error {
func (s *WebhookService) Register(oldService types.Service, client *mautrix.Client) error {
if s.RealmID == "" || s.ClientUserID == "" { if s.RealmID == "" || s.ClientUserID == "" {
return fmt.Errorf("RealmID and ClientUserID is required") return fmt.Errorf("RealmID and ClientUserID is required")
} }
@ -249,9 +252,9 @@ func (s *WebhookService) PostRegister(oldService types.Service) {
} }
} }
func (s *WebhookService) joinWebhookRooms(client *gomatrix.Client) error {
func (s *WebhookService) joinWebhookRooms(client *mautrix.Client) error {
for roomID := range s.Rooms { for roomID := range s.Rooms {
if _, err := client.JoinRoom(roomID, "", nil); err != nil {
if _, err := client.JoinRoom(roomID.String(), "", nil); err != nil {
// TODO: Leave the rooms we successfully joined? // TODO: Leave the rooms we successfully joined?
return err return err
} }
@ -300,7 +303,7 @@ func (s *WebhookService) createHook(cli *gogithub.Client, ownerRepo string) erro
cfg["secret"] = s.SecretToken cfg["secret"] = s.SecretToken
} }
events := []string{"push", "pull_request", "issues", "issue_comment", "pull_request_review_comment"} events := []string{"push", "pull_request", "issues", "issue_comment", "pull_request_review_comment"}
_, res, err := cli.Repositories.CreateHook(owner, repo, &gogithub.Hook{
_, res, err := cli.Repositories.CreateHook(context.Background(), owner, repo, &gogithub.Hook{
Name: &name, Name: &name,
Config: cfg, Config: cfg,
Events: events, Events: events,
@ -338,7 +341,7 @@ func (s *WebhookService) deleteHook(owner, repo string) error {
// Get a list of webhooks for this owner/repo and find the one which has the // Get a list of webhooks for this owner/repo and find the one which has the
// same endpoint URL which is what github uses to determine equivalence. // same endpoint URL which is what github uses to determine equivalence.
hooks, _, err := cli.Repositories.ListHooks(owner, repo, nil)
hooks, _, err := cli.Repositories.ListHooks(context.Background(), owner, repo, nil)
if err != nil { if err != nil {
return err return err
} }
@ -362,7 +365,7 @@ func (s *WebhookService) deleteHook(owner, repo string) error {
return fmt.Errorf("Failed to find hook with endpoint: %s", s.webhookEndpointURL) return fmt.Errorf("Failed to find hook with endpoint: %s", s.webhookEndpointURL)
} }
_, err = cli.Repositories.DeleteHook(owner, repo, *hook.ID)
_, err = cli.Repositories.DeleteHook(context.Background(), owner, repo, *hook.ID)
return err return err
} }
@ -427,7 +430,7 @@ func difference(a, b []string) (onlyA, onlyB []string) {
} }
} }
func (s *WebhookService) githubClientFor(userID string, allowUnauth bool) *gogithub.Client {
func (s *WebhookService) githubClientFor(userID id.UserID, allowUnauth bool) *gogithub.Client {
token, err := getTokenForUser(s.RealmID, userID) token, err := getTokenForUser(s.RealmID, userID)
if err != nil { if err != nil {
log.WithFields(log.Fields{ log.WithFields(log.Fields{
@ -462,7 +465,7 @@ func (s *WebhookService) loadRealm() (types.AuthRealm, error) {
} }
func init() { func init() {
types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
types.RegisterService(func(serviceID string, serviceUserID id.UserID, webhookEndpointURL string) types.Service {
return &WebhookService{ return &WebhookService{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, WebhookServiceType), DefaultService: types.NewDefaultService(serviceID, serviceUserID, WebhookServiceType),
webhookEndpointURL: webhookEndpointURL, webhookEndpointURL: webhookEndpointURL,

9
services/github/github_webhook_test.go

@ -13,7 +13,8 @@ import (
"github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils" "github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types" "github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
) )
var roomID = "!testroom:id" var roomID = "!testroom:id"
@ -22,13 +23,13 @@ func TestGithubWebhook(t *testing.T) {
database.SetServiceDB(&database.NopStorage{}) database.SetServiceDB(&database.NopStorage{})
// Intercept message sending to Matrix and mock responses // Intercept message sending to Matrix and mock responses
msgs := []gomatrix.TextMessage{}
msgs := []mevt.MessageEventContent{}
matrixTrans := struct{ testutils.MockTransport }{} matrixTrans := struct{ testutils.MockTransport }{}
matrixTrans.RT = func(req *http.Request) (*http.Response, error) { matrixTrans.RT = func(req *http.Request) (*http.Response, error) {
if !strings.Contains(req.URL.String(), "/send/m.room.message") { if !strings.Contains(req.URL.String(), "/send/m.room.message") {
return nil, fmt.Errorf("Unhandled URL: %s", req.URL.String()) return nil, fmt.Errorf("Unhandled URL: %s", req.URL.String())
} }
var msg gomatrix.TextMessage
var msg mevt.MessageEventContent
if err := json.NewDecoder(req.Body).Decode(&msg); err != nil { if err := json.NewDecoder(req.Body).Decode(&msg); err != nil {
return nil, fmt.Errorf("Failed to decode request JSON: %s", err) return nil, fmt.Errorf("Failed to decode request JSON: %s", err)
} }
@ -38,7 +39,7 @@ func TestGithubWebhook(t *testing.T) {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"event_id":"$yup:event"}`)), Body: ioutil.NopCloser(bytes.NewBufferString(`{"event_id":"$yup:event"}`)),
}, nil }, nil
} }
matrixCli, _ := gomatrix.NewClient("https://hyrule", "@ghwebhook:hyrule", "its_a_secret")
matrixCli, _ := mautrix.NewClient("https://hyrule", "@ghwebhook:hyrule", "its_a_secret")
matrixCli.Client = &http.Client{Transport: matrixTrans} matrixCli.Client = &http.Client{Transport: matrixTrans}
// create the service // create the service

7
services/github/webhook/webhook.go

@ -12,16 +12,17 @@ import (
"strings" "strings"
"github.com/google/go-github/github" "github.com/google/go-github/github"
"github.com/matrix-org/gomatrix"
"github.com/matrix-org/go-neb/services/utils"
"github.com/matrix-org/util" "github.com/matrix-org/util"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
mevt "maunium.net/go/mautrix/event"
) )
// OnReceiveRequest processes incoming github webhook requests and returns a // OnReceiveRequest processes incoming github webhook requests and returns a
// matrix message to send, along with parsed repo information. // matrix message to send, along with parsed repo information.
// The secretToken, if supplied, will be used to verify the request is from // The secretToken, if supplied, will be used to verify the request is from
// Github. If it isn't, an error is returned. // Github. If it isn't, an error is returned.
func OnReceiveRequest(r *http.Request, secretToken string) (string, *github.Repository, *gomatrix.HTMLMessage, *util.JSONResponse) {
func OnReceiveRequest(r *http.Request, secretToken string) (string, *github.Repository, *mevt.MessageEventContent, *util.JSONResponse) {
// Verify the HMAC signature if NEB was configured with a secret token // Verify the HMAC signature if NEB was configured with a secret token
eventType := r.Header.Get("X-GitHub-Event") eventType := r.Header.Get("X-GitHub-Event")
signatureSHA1 := r.Header.Get("X-Hub-Signature") signatureSHA1 := r.Header.Get("X-Hub-Signature")
@ -72,7 +73,7 @@ func OnReceiveRequest(r *http.Request, secretToken string) (string, *github.Repo
return "", nil, nil, &resErr return "", nil, nil, &resErr
} }
msg := gomatrix.GetHTMLMessage("m.notice", htmlStr)
msg := utils.StrippedHTMLMessage(mevt.MsgNotice, htmlStr)
return refinedType, repo, &msg, nil return refinedType, repo, &msg, nil
} }

21
services/utils/utils.go

@ -0,0 +1,21 @@
package utils
import (
"html"
"regexp"
mevt "maunium.net/go/mautrix/event"
)
var htmlRegex = regexp.MustCompile("<[^<]+?>")
// StrippedHTMLMessage returns a MessageEventContent with the body set to a stripped version of the provided HTML,
// in addition to the provided HTML.
func StrippedHTMLMessage(msgtype mevt.MessageType, htmlText string) mevt.MessageEventContent {
return mevt.MessageEventContent{
Body: html.UnescapeString(htmlRegex.ReplaceAllLiteralString(htmlText, "")),
MsgType: msgtype,
Format: mevt.FormatHTML,
FormattedBody: htmlText,
}
}

19
services/utils/utils_test.go

@ -0,0 +1,19 @@
package utils
import (
"testing"
mevt "maunium.net/go/mautrix/event"
)
func TestHTMLStrip(t *testing.T) {
msg := `before &lt;<hello a="b"><inside />during</hello>&gt; after`
stripped := StrippedHTMLMessage(mevt.MsgNotice, msg)
if stripped.MsgType != mevt.MsgNotice {
t.Fatalf("Expected MsgType %v, got %v", mevt.MsgNotice, stripped.MsgType)
}
expected := "before <during> after"
if stripped.Body != expected {
t.Fatalf(`Expected Body "%v", got "%v"`, expected, stripped.Body)
}
}
Loading…
Cancel
Save