Browse Source

[WIP] Switch Gomatrix for Mautrix (#322)

* Switch core functionality to Mautrix

Signed-off-by: Nikos Filippakis <me@nfil.dev>

* Convert and re-enable rssbot

Signed-off-by: Nikos Filippakis <me@nfil.dev>

* Convert and re-enable giphy service

Signed-off-by: Nikos Filippakis <me@nfil.dev>

* Convert and re-enable wikipedia, imgur and guggy services

Signed-off-by: Nikos Filippakis <me@nfil.dev>

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

Signed-off-by: Nikos Filippakis <me@nfil.dev>

* Convert and add the rest of the services

Re-enables the services: alertmanager, google, jira, slackapi, travisci

Signed-off-by: Nikos Filippakis <me@nfil.dev>
pull/327/head
Nikos Filippakis 4 years ago
committed by GitHub
parent
commit
6409b00205
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      .travis.yml
  2. 10
      Dockerfile
  3. 4
      README.md
  4. 10
      api/api.go
  5. 5
      api/handlers/auth.go
  6. 4
      api/handlers/service.go
  7. 81
      clients/clients.go
  8. 34
      clients/clients_test.go
  9. 18
      database/db.go
  10. 29
      database/interface.go
  11. 27
      database/schema.go
  12. 14
      go.mod
  13. 74
      go.sum
  14. 3
      goneb.go
  15. 11
      matrix/matrix.go
  16. 14
      realms/github/github.go
  17. 15
      realms/jira/jira.go
  18. 37
      services/alertmanager/alertmanager.go
  19. 18
      services/alertmanager/alertmanager_test.go
  20. 15
      services/echo/echo.go
  21. 29
      services/giphy/giphy.go
  22. 215
      services/github/github.go
  23. 29
      services/github/github_webhook.go
  24. 9
      services/github/github_webhook_test.go
  25. 7
      services/github/webhook/webhook.go
  26. 48
      services/google/google.go
  27. 4
      services/google/google_test.go
  28. 30
      services/guggy/guggy.go
  29. 4
      services/guggy/guggy_test.go
  30. 50
      services/imgur/imgur.go
  31. 4
      services/imgur/imgur_test.go
  32. 43
      services/jira/jira.go
  33. 7
      services/jira/webhook/webhook.go
  34. 28
      services/rssbot/rssbot.go
  35. 10
      services/rssbot/rssbot_test.go
  36. 4
      services/slackapi/message.go
  37. 22
      services/slackapi/slackapi.go
  38. 20
      services/travisci/travisci.go
  39. 13
      services/travisci/travisci_test.go
  40. 21
      services/utils/utils.go
  41. 19
      services/utils/utils_test.go
  42. 28
      services/wikipedia/wikipedia.go
  43. 4
      services/wikipedia/wikipedia_test.go
  44. 6
      testutil_test.go
  45. 6
      types/actions.go
  46. 8
      types/auth.go
  47. 41
      types/service.go

4
.travis.yml

@ -1,7 +1,11 @@
os: linux
dist: bionic
language: go
go:
- 1.14
install:
- sudo apt-get update
- sudo apt-get -y install libolm2 libolm-dev
- go get golang.org/x/lint/golint
- go get github.com/fzipp/gocyclo

10
Dockerfile

@ -1,7 +1,11 @@
# Build go-neb
FROM golang:1.14-alpine as builder
RUN apk add --no-cache -t build-deps git gcc musl-dev go
RUN apk add --no-cache -t build-deps git gcc musl-dev go make g++
RUN git clone https://gitlab.matrix.org/matrix-org/olm.git /tmp/libolm \
&& cd /tmp/libolm \
&& make install
COPY . /tmp/go-neb
WORKDIR /tmp/go-neb
@ -22,7 +26,11 @@ ENV BIND_ADDRESS=:4050 \
GID=1337
COPY --from=builder /tmp/go-neb/go-neb /usr/local/bin/go-neb
# Copy libolm.so
COPY --from=builder /usr/local/lib/* /usr/local/lib/
RUN apk add --no-cache \
libstdc++ \
ca-certificates \
su-exec \
s6

4
README.md

@ -169,6 +169,10 @@ Before submitting pull requests, please read the [Matrix.org contribution guidel
# Developing
This project depends on `libolm` for the end-to-end encryption. Therefore,
you need to install `libolm2` and `libolm-dev` on Ubuntu / `libolm-devel` on
CentOS to be able to build and run it.
There's a bunch more tools this project uses when developing in order to do
things like linting. Some of them are bundled with go (fmt and vet) but some
are not. You should install the ones which are not:

10
api/api.go

@ -12,6 +12,8 @@ import (
"encoding/json"
"errors"
"net/url"
"maunium.net/go/mautrix/id"
)
// ConfigureAuthRealmRequest is a request to /configureAuthRealm
@ -32,7 +34,7 @@ type RequestAuthSessionRequest struct {
RealmID string
// The Matrix user ID requesting the auth session. If the auth is successful,
// this user ID will be associated with the third-party credentials obtained.
UserID string
UserID id.UserID
// AuthRealm specific config information. See the docs for the auth realm you're interested in.
Config json.RawMessage
}
@ -47,7 +49,7 @@ type ConfigureServiceRequest struct {
Type string
// The user ID of the configured client that this service will use to communicate with Matrix.
// The user MUST already be configured.
UserID string
UserID id.UserID
// Service-specific config information. See the docs for the service you're interested in.
Config json.RawMessage
}
@ -56,7 +58,7 @@ type ConfigureServiceRequest struct {
// Go-NEB can drive it. It forms the HTTP body to /configureClient requests.
type ClientConfig struct {
// The matrix User ID to connect with. E.g. @alice:matrix.org
UserID string
UserID id.UserID
// A URL with the host and port of the matrix server. E.g. https://matrix.org:8448
HomeserverURL string
// The matrix access token to authenticate the requests with.
@ -81,7 +83,7 @@ type ClientConfig struct {
type Session struct {
SessionID string
RealmID string
UserID string
UserID id.UserID
Config json.RawMessage
}

5
api/handlers/auth.go

@ -13,6 +13,7 @@ import (
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/util"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix/id"
)
// RequestAuthSession represents an HTTP handler capable of processing /admin/requestAuthSession requests.
@ -104,7 +105,7 @@ func (h *RemoveAuthSession) OnIncomingRequest(req *http.Request) util.JSONRespon
}
var body struct {
RealmID string
UserID string
UserID id.UserID
}
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
return util.MessageResponse(400, "Error parsing request JSON")
@ -276,7 +277,7 @@ func (h *GetSession) OnIncomingRequest(req *http.Request) util.JSONResponse {
}
var body struct {
RealmID string
UserID string
UserID id.UserID
}
if err := json.NewDecoder(req.Body).Decode(&body); err != nil {
return util.MessageResponse(400, "Error parsing request JSON")

4
api/handlers/service.go

@ -14,9 +14,9 @@ import (
"github.com/matrix-org/go-neb/metrics"
"github.com/matrix-org/go-neb/polling"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"github.com/matrix-org/util"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
)
// ConfigureService represents an HTTP handler which can process /admin/configureService requests.
@ -225,7 +225,7 @@ func (h *GetService) OnIncomingRequest(req *http.Request) util.JSONResponse {
}
}
func checkClientForService(service types.Service, client *gomatrix.Client) error {
func checkClientForService(service types.Service, client *mautrix.Client) error {
// If there are any commands or expansions for this Service then the service user ID
// MUST be a syncing client or else the Service will never get the incoming command/expansion!
cmds := service.Commands(client)

81
clients/clients.go

@ -13,9 +13,11 @@ import (
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/metrics"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
shellwords "github.com/mattn/go-shellwords"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// A Clients is a collection of clients used for bot services.
@ -24,7 +26,7 @@ type Clients struct {
httpClient *http.Client
dbMutex sync.Mutex
mapMutex sync.Mutex
clients map[string]clientEntry
clients map[id.UserID]clientEntry
}
// New makes a new collection of matrix clients
@ -32,13 +34,13 @@ func New(db database.Storer, cli *http.Client) *Clients {
clients := &Clients{
db: db,
httpClient: cli,
clients: make(map[string]clientEntry), // user_id => clientEntry
clients: make(map[id.UserID]clientEntry), // user_id => clientEntry
}
return clients
}
// Client gets a client for the userID
func (c *Clients) Client(userID string) (*gomatrix.Client, error) {
func (c *Clients) Client(userID id.UserID) (*mautrix.Client, error) {
entry := c.getClient(userID)
if entry.client != nil {
return entry.client, nil
@ -71,10 +73,10 @@ func (c *Clients) Start() error {
type clientEntry struct {
config api.ClientConfig
client *gomatrix.Client
client *mautrix.Client
}
func (c *Clients) getClient(userID string) clientEntry {
func (c *Clients) getClient(userID id.UserID) clientEntry {
c.mapMutex.Lock()
defer c.mapMutex.Unlock()
return c.clients[userID]
@ -86,7 +88,7 @@ func (c *Clients) setClient(client clientEntry) {
c.clients[client.config.UserID] = client
}
func (c *Clients) loadClientFromDB(userID string) (entry clientEntry, err error) {
func (c *Clients) loadClientFromDB(userID id.UserID) (entry clientEntry, err error) {
c.dbMutex.Lock()
defer c.dbMutex.Unlock()
@ -153,7 +155,7 @@ func (c *Clients) updateClientInDB(newConfig api.ClientConfig) (new clientEntry,
return
}
func (c *Clients) onMessageEvent(client *gomatrix.Client, event *gomatrix.Event) {
func (c *Clients) onMessageEvent(client *mautrix.Client, event *mevt.Event) {
services, err := c.db.LoadServicesForUser(client.UserID)
if err != nil {
log.WithFields(log.Fields{
@ -163,13 +165,19 @@ func (c *Clients) onMessageEvent(client *gomatrix.Client, event *gomatrix.Event)
}).Warn("Error loading services")
}
body, ok := event.Body()
if !ok || body == "" {
if err := event.Content.ParseRaw(mevt.EventMessage); err != nil {
return
}
message := event.Content.AsMessage()
body := message.Body
if body == "" {
return
}
// filter m.notice to prevent loops
if msgtype, ok := event.MessageType(); !ok || msgtype == "m.notice" {
if message.MsgType == mevt.MsgNotice {
return
}
@ -198,7 +206,7 @@ func (c *Clients) onMessageEvent(client *gomatrix.Client, event *gomatrix.Event)
}
for _, content := range responses {
if _, err := client.SendMessageEvent(event.RoomID, "m.room.message", content); err != nil {
if _, err := client.SendMessageEvent(event.RoomID, mevt.EventMessage, content); err != nil {
log.WithFields(log.Fields{
log.ErrorKey: err,
"room_id": event.RoomID,
@ -213,7 +221,7 @@ func (c *Clients) onMessageEvent(client *gomatrix.Client, event *gomatrix.Event)
// the matching command with the longest path. Returns the JSON encodable
// content of a single matrix message event to use as a response or nil if no
// response is appropriate.
func runCommandForService(cmds []types.Command, event *gomatrix.Event, arguments []string) interface{} {
func runCommandForService(cmds []types.Command, event *mevt.Event, arguments []string) interface{} {
var bestMatch *types.Command
for i, command := range cmds {
matches := command.Matches(arguments)
@ -245,7 +253,10 @@ func runCommandForService(cmds []types.Command, event *gomatrix.Event, arguments
}).Warn("Command returned both error and content.")
}
metrics.IncrementCommand(bestMatch.Path[0], metrics.StatusFailure)
content = gomatrix.TextMessage{"m.notice", err.Error()}
content = mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: err.Error(),
}
} else {
metrics.IncrementCommand(bestMatch.Path[0], metrics.StatusSuccess)
}
@ -254,7 +265,7 @@ func runCommandForService(cmds []types.Command, event *gomatrix.Event, arguments
}
// run the expansions for a matrix event.
func runExpansionsForService(expans []types.Expansion, event *gomatrix.Event, body string) []interface{} {
func runExpansionsForService(expans []types.Expansion, event *mevt.Event, body string) []interface{} {
var responses []interface{}
for _, expansion := range expans {
@ -275,10 +286,13 @@ func runExpansionsForService(expans []types.Expansion, event *gomatrix.Event, bo
return responses
}
func (c *Clients) onBotOptionsEvent(client *gomatrix.Client, event *gomatrix.Event) {
func (c *Clients) onBotOptionsEvent(client *mautrix.Client, event *mevt.Event) {
// see if these options are for us. The state key is the user ID with a leading _
// to get around restrictions in the HS about having user IDs as state keys.
targetUserID := strings.TrimPrefix(*event.StateKey, "_")
if event.StateKey == nil {
return
}
targetUserID := id.UserID(strings.TrimPrefix(*event.StateKey, "_"))
if targetUserID != client.UserID {
return
}
@ -287,7 +301,7 @@ func (c *Clients) onBotOptionsEvent(client *gomatrix.Client, event *gomatrix.Eve
UserID: client.UserID,
RoomID: event.RoomID,
SetByUserID: event.Sender,
Options: event.Content,
Options: event.Content.Raw,
}
if _, err := c.db.StoreBotOptions(opts); err != nil {
log.WithFields(log.Fields{
@ -299,15 +313,14 @@ func (c *Clients) onBotOptionsEvent(client *gomatrix.Client, event *gomatrix.Eve
}
}
func (c *Clients) onRoomMemberEvent(client *gomatrix.Client, event *gomatrix.Event) {
if *event.StateKey != client.UserID {
return // not our member event
}
m := event.Content["membership"]
membership, ok := m.(string)
if !ok {
func (c *Clients) onRoomMemberEvent(client *mautrix.Client, event *mevt.Event) {
if err := event.Content.ParseRaw(mevt.StateMember); err != nil {
return
}
if event.StateKey == nil || *event.StateKey != client.UserID.String() {
return // not our member event
}
membership := event.Content.AsMember().Membership
if membership == "invite" {
logger := log.WithFields(log.Fields{
"room_id": event.RoomID,
@ -317,10 +330,10 @@ func (c *Clients) onRoomMemberEvent(client *gomatrix.Client, event *gomatrix.Eve
logger.Print("Accepting invite from user")
content := struct {
Inviter string `json:"inviter"`
Inviter id.UserID `json:"inviter"`
}{event.Sender}
if _, err := client.JoinRoom(event.RoomID, "", content); err != nil {
if _, err := client.JoinRoom(event.RoomID.String(), "", content); err != nil {
logger.WithError(err).Print("Failed to join room")
} else {
logger.Print("Joined room")
@ -328,15 +341,15 @@ func (c *Clients) onRoomMemberEvent(client *gomatrix.Client, event *gomatrix.Eve
}
}
func (c *Clients) newClient(config api.ClientConfig) (*gomatrix.Client, error) {
client, err := gomatrix.NewClient(config.HomeserverURL, config.UserID, config.AccessToken)
func (c *Clients) newClient(config api.ClientConfig) (*mautrix.Client, error) {
client, err := mautrix.NewClient(config.HomeserverURL, config.UserID, config.AccessToken)
if err != nil {
return nil, err
}
client.Client = c.httpClient
syncer := client.Syncer.(*gomatrix.DefaultSyncer)
syncer := client.Syncer.(*mautrix.DefaultSyncer)
nebStore := &matrix.NEBStore{
InMemoryStore: *gomatrix.NewInMemoryStore(),
InMemoryStore: *mautrix.NewInMemoryStore(),
Database: c.db,
ClientConfig: config,
}
@ -346,16 +359,16 @@ func (c *Clients) newClient(config api.ClientConfig) (*gomatrix.Client, error) {
// TODO: Check that the access token is valid for the userID by peforming
// a request against the server.
syncer.OnEventType("m.room.message", func(event *gomatrix.Event) {
syncer.OnEventType(mevt.EventMessage, func(event *mevt.Event) {
c.onMessageEvent(client, event)
})
syncer.OnEventType("m.room.bot.options", func(event *gomatrix.Event) {
syncer.OnEventType(mevt.Type{Type: "m.room.bot.options", Class: mevt.UnknownEventType}, func(event *mevt.Event) {
c.onBotOptionsEvent(client, event)
})
if config.AutoJoinRooms {
syncer.OnEventType("m.room.member", func(event *gomatrix.Event) {
syncer.OnEventType(mevt.StateMember, func(event *mevt.Event) {
c.onRoomMemberEvent(client, event)
})
}

34
clients/clients_test.go

@ -8,7 +8,9 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
var commandParseTests = []struct {
@ -28,7 +30,7 @@ type MockService struct {
commands []types.Command
}
func (s *MockService) Commands(cli *gomatrix.Client) []types.Command {
func (s *MockService) Commands(cli *mautrix.Client) []types.Command {
return s.commands
}
@ -37,7 +39,7 @@ type MockStore struct {
service types.Service
}
func (d *MockStore) LoadServicesForUser(userID string) ([]types.Service, error) {
func (d *MockStore) LoadServicesForUser(userID id.UserID) ([]types.Service, error) {
return []types.Service{d.service}, nil
}
@ -54,7 +56,7 @@ func TestCommandParsing(t *testing.T) {
cmds := []types.Command{
types.Command{
Path: []string{"test"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
executedCmdArgs = args
return nil, nil
},
@ -72,19 +74,25 @@ func TestCommandParsing(t *testing.T) {
Transport: trans,
}
clients := New(&store, cli)
mxCli, _ := gomatrix.NewClient("https://someplace.somewhere", "@service:user", "token")
mxCli, _ := mautrix.NewClient("https://someplace.somewhere", "@service:user", "token")
mxCli.Client = cli
for _, input := range commandParseTests {
executedCmdArgs = []string{}
event := gomatrix.Event{
Type: "m.room.message",
Sender: "@someone:somewhere",
RoomID: "!foo:bar",
Content: map[string]interface{}{
"body": input.body,
"msgtype": "m.text",
},
content := mevt.Content{Raw: map[string]interface{}{
"body": input.body,
"msgtype": "m.text",
}}
if veryRaw, err := content.MarshalJSON(); err != nil {
t.Errorf("Error marshalling JSON: %s", err)
} else {
content.VeryRaw = veryRaw
}
event := mevt.Event{
Type: mevt.EventMessage,
Sender: "@someone:somewhere",
RoomID: "!foo:bar",
Content: content,
}
clients.onMessageEvent(mxCli, &event)
if !reflect.DeepEqual(executedCmdArgs, input.expectArgs) {

18
database/db.go

@ -4,9 +4,11 @@ import (
"database/sql"
"encoding/json"
"fmt"
"time"
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/types"
"time"
"maunium.net/go/mautrix/id"
)
// A ServiceDB stores the configuration for the services
@ -75,7 +77,7 @@ func (d *ServiceDB) LoadMatrixClientConfigs() (configs []api.ClientConfig, err e
// LoadMatrixClientConfig loads a Matrix client config from the database.
// Returns sql.ErrNoRows if the client isn't in the database.
func (d *ServiceDB) LoadMatrixClientConfig(userID string) (config api.ClientConfig, err error) {
func (d *ServiceDB) LoadMatrixClientConfig(userID id.UserID) (config api.ClientConfig, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
config, err = selectMatrixClientConfigTxn(txn, userID)
return err
@ -84,7 +86,7 @@ func (d *ServiceDB) LoadMatrixClientConfig(userID string) (config api.ClientConf
}
// UpdateNextBatch updates the next_batch token for the given user.
func (d *ServiceDB) UpdateNextBatch(userID, nextBatch string) (err error) {
func (d *ServiceDB) UpdateNextBatch(userID id.UserID, nextBatch string) (err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
return updateNextBatchTxn(txn, userID, nextBatch)
})
@ -92,7 +94,7 @@ func (d *ServiceDB) UpdateNextBatch(userID, nextBatch string) (err error) {
}
// LoadNextBatch loads the next_batch token for the given user.
func (d *ServiceDB) LoadNextBatch(userID string) (nextBatch string, err error) {
func (d *ServiceDB) LoadNextBatch(userID id.UserID) (nextBatch string, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
nextBatch, err = selectNextBatchTxn(txn, userID)
return err
@ -120,7 +122,7 @@ func (d *ServiceDB) DeleteService(serviceID string) (err error) {
// LoadServicesForUser loads all the bot services configured for a given user.
// Returns an empty list if there aren't any services configured.
func (d *ServiceDB) LoadServicesForUser(serviceUserID string) (services []types.Service, err error) {
func (d *ServiceDB) LoadServicesForUser(serviceUserID id.UserID) (services []types.Service, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
services, err = selectServicesForUserTxn(txn, serviceUserID)
if err != nil {
@ -218,7 +220,7 @@ func (d *ServiceDB) StoreAuthSession(session types.AuthSession) (old types.AuthS
// RemoveAuthSession removes the auth session for the given user on the given realm.
// No error is returned if the session did not exist in the first place.
func (d *ServiceDB) RemoveAuthSession(realmID, userID string) error {
func (d *ServiceDB) RemoveAuthSession(realmID string, userID id.UserID) error {
return runTransaction(d.db, func(txn *sql.Tx) error {
return deleteAuthSessionTxn(txn, realmID, userID)
})
@ -227,7 +229,7 @@ func (d *ServiceDB) RemoveAuthSession(realmID, userID string) error {
// LoadAuthSessionByUser loads an AuthSession from the database based on the given
// realm and user ID.
// Returns sql.ErrNoRows if the session isn't in the database.
func (d *ServiceDB) LoadAuthSessionByUser(realmID, userID string) (session types.AuthSession, err error) {
func (d *ServiceDB) LoadAuthSessionByUser(realmID string, userID id.UserID) (session types.AuthSession, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
session, err = selectAuthSessionByUserTxn(txn, realmID, userID)
return err
@ -248,7 +250,7 @@ func (d *ServiceDB) LoadAuthSessionByID(realmID, sessionID string) (session type
// LoadBotOptions loads bot options from the database.
// Returns sql.ErrNoRows if the bot options isn't in the database.
func (d *ServiceDB) LoadBotOptions(userID, roomID string) (opts types.BotOptions, err error) {
func (d *ServiceDB) LoadBotOptions(userID id.UserID, roomID id.RoomID) (opts types.BotOptions, err error) {
err = runTransaction(d.db, func(txn *sql.Tx) error {
opts, err = selectBotOptionsTxn(txn, userID, roomID)
return err

29
database/interface.go

@ -3,20 +3,21 @@ package database
import (
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/types"
"maunium.net/go/mautrix/id"
)
// Storer is the interface which needs to be conformed to in order to persist Go-NEB data
type Storer interface {
StoreMatrixClientConfig(config api.ClientConfig) (oldConfig api.ClientConfig, err error)
LoadMatrixClientConfigs() (configs []api.ClientConfig, err error)
LoadMatrixClientConfig(userID string) (config api.ClientConfig, err error)
LoadMatrixClientConfig(userID id.UserID) (config api.ClientConfig, err error)
UpdateNextBatch(userID, nextBatch string) (err error)
LoadNextBatch(userID string) (nextBatch string, err error)
UpdateNextBatch(userID id.UserID, nextBatch string) (err error)
LoadNextBatch(userID id.UserID) (nextBatch string, err error)
LoadService(serviceID string) (service types.Service, err error)
DeleteService(serviceID string) (err error)
LoadServicesForUser(serviceUserID string) (services []types.Service, err error)
LoadServicesForUser(serviceUserID id.UserID) (services []types.Service, err error)
LoadServicesByType(serviceType string) (services []types.Service, err error)
StoreService(service types.Service) (oldService types.Service, err error)
@ -25,11 +26,11 @@ type Storer interface {
StoreAuthRealm(realm types.AuthRealm) (old types.AuthRealm, err error)
StoreAuthSession(session types.AuthSession) (old types.AuthSession, err error)
LoadAuthSessionByUser(realmID, userID string) (session types.AuthSession, err error)
LoadAuthSessionByUser(realmID string, userID id.UserID) (session types.AuthSession, err error)
LoadAuthSessionByID(realmID, sessionID string) (session types.AuthSession, err error)
RemoveAuthSession(realmID, userID string) error
RemoveAuthSession(realmID string, userID id.UserID) error
LoadBotOptions(userID, roomID string) (opts types.BotOptions, err error)
LoadBotOptions(userID id.UserID, roomID id.RoomID) (opts types.BotOptions, err error)
StoreBotOptions(opts types.BotOptions) (oldOpts types.BotOptions, err error)
InsertFromConfig(cfg *api.ConfigFile) error
@ -50,17 +51,17 @@ func (s *NopStorage) LoadMatrixClientConfigs() (configs []api.ClientConfig, err
}
// LoadMatrixClientConfig NOP
func (s *NopStorage) LoadMatrixClientConfig(userID string) (config api.ClientConfig, err error) {
func (s *NopStorage) LoadMatrixClientConfig(userID id.UserID) (config api.ClientConfig, err error) {
return
}
// UpdateNextBatch NOP
func (s *NopStorage) UpdateNextBatch(userID, nextBatch string) (err error) {
func (s *NopStorage) UpdateNextBatch(userID id.UserID, nextBatch string) (err error) {
return
}
// LoadNextBatch NOP
func (s *NopStorage) LoadNextBatch(userID string) (nextBatch string, err error) {
func (s *NopStorage) LoadNextBatch(userID id.UserID) (nextBatch string, err error) {
return
}
@ -75,7 +76,7 @@ func (s *NopStorage) DeleteService(serviceID string) (err error) {
}
// LoadServicesForUser NOP
func (s *NopStorage) LoadServicesForUser(serviceUserID string) (services []types.Service, err error) {
func (s *NopStorage) LoadServicesForUser(serviceUserID id.UserID) (services []types.Service, err error) {
return
}
@ -110,7 +111,7 @@ func (s *NopStorage) StoreAuthSession(session types.AuthSession) (old types.Auth
}
// LoadAuthSessionByUser NOP
func (s *NopStorage) LoadAuthSessionByUser(realmID, userID string) (session types.AuthSession, err error) {
func (s *NopStorage) LoadAuthSessionByUser(realmID string, userID id.UserID) (session types.AuthSession, err error) {
return
}
@ -120,12 +121,12 @@ func (s *NopStorage) LoadAuthSessionByID(realmID, sessionID string) (session typ
}
// RemoveAuthSession NOP
func (s *NopStorage) RemoveAuthSession(realmID, userID string) error {
func (s *NopStorage) RemoveAuthSession(realmID string, userID id.UserID) error {
return nil
}
// LoadBotOptions NOP
func (s *NopStorage) LoadBotOptions(userID, roomID string) (opts types.BotOptions, err error) {
func (s *NopStorage) LoadBotOptions(userID id.UserID, roomID id.RoomID) (opts types.BotOptions, err error) {
return
}

27
database/schema.go

@ -8,6 +8,7 @@ import (
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/types"
"maunium.net/go/mautrix/id"
)
const schemaSQL = `
@ -66,7 +67,7 @@ const selectMatrixClientConfigSQL = `
SELECT client_json FROM matrix_clients WHERE user_id = $1
`
func selectMatrixClientConfigTxn(txn *sql.Tx, userID string) (config api.ClientConfig, err error) {
func selectMatrixClientConfigTxn(txn *sql.Tx, userID id.UserID) (config api.ClientConfig, err error) {
var configJSON []byte
err = txn.QueryRow(selectMatrixClientConfigSQL, userID).Scan(&configJSON)
if err != nil {
@ -135,7 +136,7 @@ const updateNextBatchSQL = `
UPDATE matrix_clients SET next_batch = $1 WHERE user_id = $2
`
func updateNextBatchTxn(txn *sql.Tx, userID, nextBatch string) error {
func updateNextBatchTxn(txn *sql.Tx, userID id.UserID, nextBatch string) error {
_, err := txn.Exec(updateNextBatchSQL, nextBatch, userID)
return err
}
@ -144,7 +145,7 @@ const selectNextBatchSQL = `
SELECT next_batch FROM matrix_clients WHERE user_id = $1
`
func selectNextBatchTxn(txn *sql.Tx, userID string) (string, error) {
func selectNextBatchTxn(txn *sql.Tx, userID id.UserID) (string, error) {
var nextBatch string
row := txn.QueryRow(selectNextBatchSQL, userID)
if err := row.Scan(&nextBatch); err != nil {
@ -160,7 +161,7 @@ SELECT service_type, service_user_id, service_json FROM services
func selectServiceTxn(txn *sql.Tx, serviceID string) (types.Service, error) {
var serviceType string
var serviceUserID string
var serviceUserID id.UserID
var serviceJSON []byte
row := txn.QueryRow(selectServiceSQL, serviceID)
if err := row.Scan(&serviceType, &serviceUserID, &serviceJSON); err != nil {
@ -210,7 +211,7 @@ const selectServicesForUserSQL = `
SELECT service_id, service_type, service_json FROM services WHERE service_user_id=$1 ORDER BY service_id
`
func selectServicesForUserTxn(txn *sql.Tx, userID string) (srvs []types.Service, err error) {
func selectServicesForUserTxn(txn *sql.Tx, userID id.UserID) (srvs []types.Service, err error) {
rows, err := txn.Query(selectServicesForUserSQL, userID)
if err != nil {
return
@ -246,7 +247,7 @@ func selectServicesByTypeTxn(txn *sql.Tx, serviceType string) (srvs []types.Serv
for rows.Next() {
var s types.Service
var serviceID string
var serviceUserID string
var serviceUserID id.UserID
var serviceJSON []byte
if err = rows.Scan(&serviceID, &serviceUserID, &serviceJSON); err != nil {
return
@ -369,7 +370,7 @@ const deleteAuthSessionSQL = `
DELETE FROM auth_sessions WHERE realm_id=$1 AND user_id=$2
`
func deleteAuthSessionTxn(txn *sql.Tx, realmID, userID string) error {
func deleteAuthSessionTxn(txn *sql.Tx, realmID string, userID id.UserID) error {
_, err := txn.Exec(deleteAuthSessionSQL, realmID, userID)
return err
}
@ -380,7 +381,7 @@ SELECT session_id, realm_type, realm_json, session_json FROM auth_sessions
WHERE auth_sessions.realm_id = $1 AND auth_sessions.user_id = $2
`
func selectAuthSessionByUserTxn(txn *sql.Tx, realmID, userID string) (types.AuthSession, error) {
func selectAuthSessionByUserTxn(txn *sql.Tx, realmID string, userID id.UserID) (types.AuthSession, error) {
var id string
var realmType string
var realmJSON []byte
@ -409,12 +410,12 @@ SELECT user_id, realm_type, realm_json, session_json FROM auth_sessions
WHERE auth_sessions.realm_id = $1 AND auth_sessions.session_id = $2
`
func selectAuthSessionByIDTxn(txn *sql.Tx, realmID, id string) (types.AuthSession, error) {
var userID string
func selectAuthSessionByIDTxn(txn *sql.Tx, realmID, sid string) (types.AuthSession, error) {
var userID id.UserID
var realmType string
var realmJSON []byte
var sessionJSON []byte
row := txn.QueryRow(selectAuthSessionByIDSQL, realmID, id)
row := txn.QueryRow(selectAuthSessionByIDSQL, realmID, sid)
if err := row.Scan(&userID, &realmType, &realmJSON, &sessionJSON); err != nil {
return nil, err
}
@ -422,7 +423,7 @@ func selectAuthSessionByIDTxn(txn *sql.Tx, realmID, id string) (types.AuthSessio
if err != nil {
return nil, err
}
session := realm.AuthSession(id, userID, realmID)
session := realm.AuthSession(sid, userID, realmID)
if session == nil {
return nil, fmt.Errorf("Cannot create session for given realm")
}
@ -454,7 +455,7 @@ const selectBotOptionsSQL = `
SELECT bot_options_json, set_by_user_id FROM bot_options WHERE user_id = $1 AND room_id = $2
`
func selectBotOptionsTxn(txn *sql.Tx, userID, roomID string) (opts types.BotOptions, err error) {
func selectBotOptionsTxn(txn *sql.Tx, userID id.UserID, roomID id.RoomID) (opts types.BotOptions, err error) {
var optionsJSON []byte
err = txn.QueryRow(selectBotOptionsSQL, userID, roomID).Scan(&optionsJSON, &opts.SetByUserID)
if err != nil {

14
go.mod

@ -17,7 +17,7 @@ require (
github.com/gogo/protobuf v1.1.1 // indirect
github.com/golang/protobuf v1.3.2 // 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/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195
github.com/json-iterator/go v1.1.9 // indirect
@ -25,7 +25,6 @@ require (
github.com/kr/pretty v0.1.0 // indirect
github.com/lib/pq v1.3.0
github.com/matrix-org/dugong v0.0.0-20180820122854-51a565b5666b
github.com/matrix-org/gomatrix v0.0.0-20200128155335-9e7906b6766d
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7
github.com/mattn/go-shellwords v1.0.10
github.com/mattn/go-sqlite3 v2.0.3+incompatible
@ -36,21 +35,22 @@ require (
github.com/modern-go/reflect2 v1.0.1 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223 // indirect
github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/pkg/errors v0.8.1 // indirect
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v0.8.1-0.20160916180340-5636dc67ae77
github.com/prometheus/client_model v0.0.0-20150212101744-fa8ad6fec335 // indirect
github.com/prometheus/common v0.0.0-20161002210234-85637ea67b04 // indirect
github.com/prometheus/procfs v0.0.0-20160411190841-abf152e5f3e9 // indirect
github.com/russross/blackfriday v1.5.2
github.com/sasha-s/go-deadlock v0.2.0
github.com/sirupsen/logrus v1.4.2
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect
github.com/stretchr/testify v1.4.0 // indirect
golang.org/x/net v0.0.0-20200301022130-244492dfa37a
golang.org/dl v0.0.0-20200601221412-a954fa24b3e5 // indirect
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e // indirect
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 // indirect
golang.org/x/tools v0.0.0-20200311090712-aafaee8bce8c // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.8
maunium.net/go/gomuks v0.1.0
maunium.net/go/mautrix v0.4.7
)

74
go.sum

@ -1,6 +1,11 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
github.com/alecthomas/chroma v0.7.2/go.mod h1:fv5SzZPFJbwp2NXJWpFIX7DZS4HgV1K4ew4Pc2OZD9s=
github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -17,7 +22,9 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dghubble/oauth1 v0.6.0 h1:m1yC01Ohc/eF38jwZ8JUjL1a+XHHXtGQgK+MxQbmSx0=
github.com/dghubble/oauth1 v0.6.0/go.mod h1:8pFdfPkv/jr8mkChVbNVuJ0suiHe278BtWI4Tk1ujxk=
@ -25,8 +32,11 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/die-net/lrucache v0.0.0-20190707192454-883874fe3947 h1:U/5Sq2nJQ0XDyks+8ATghtHSuquIGq7JYrqSrvtR2dg=
github.com/die-net/lrucache v0.0.0-20190707192454-883874fe3947/go.mod h1:KsMcjmY1UCGl7ozPbdVPDOvLaFeXnptSvtNRczhxNto=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/fatih/structs v1.0.0 h1:BrX964Rv5uQ3wwS+KRUAJCBBw5PQmgJfJ6v4yly5QwU=
github.com/fatih/structs v1.0.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
@ -39,6 +49,7 @@ github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-github v2.0.1-0.20160719063544-b5e5babef39c+incompatible h1:9bbdREkf94ZqDMJ3Nsy5cJYNswJW2Xiirp+YuuuGAKM=
github.com/google/go-github v2.0.1-0.20160719063544-b5e5babef39c+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
@ -49,6 +60,7 @@ github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+u
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135 h1:zLTLjkaOFEFIOxY5BWLFLwh+cL8vOBW4XJ2aqLE/Tf0=
github.com/google/go-querystring v0.0.0-20170111101155-53e6ce116135/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA=
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195 h1:j0UEFmS7wSjAwKEIkgKBn8PRDfjcuggzr93R9wk53nQ=
@ -59,19 +71,26 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kyokomi/emoji v2.2.2+incompatible/go.mod h1:mZ6aGCD7yk8j6QY6KICwnZ2pxoszVseX1DNoGtU2tBA=
github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU=
github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lithammer/fuzzysearch v1.1.0/go.mod h1:Bqx4wo8lTOFcJr3ckpY6HA9lEIOO0H5HrkJ5CsN56HQ=
github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/matrix-org/dugong v0.0.0-20180820122854-51a565b5666b h1:xpcmnpfUImRC4O2SAS/dmTcJENDXvGmLUzey76V1R3Q=
github.com/matrix-org/dugong v0.0.0-20180820122854-51a565b5666b/go.mod h1:NgPCr+UavRGH6n5jmdX8DuqFZ4JiCWIJoZiuhTRLSUg=
github.com/matrix-org/gomatrix v0.0.0-20200128155335-9e7906b6766d h1:Vf/EQgAfg8/CBUQv9te7UJreZ9iKKouB2gb8UIRM4jQ=
github.com/matrix-org/gomatrix v0.0.0-20200128155335-9e7906b6766d/go.mod h1:3fxX6gUjWyI/2Bt7J1OLhpCzOfO/bB3AiX0cJtEKud0=
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7 h1:ntrLa/8xVzeSs8vHFHK25k0C+NV74sYMJnNSg5NoSRo=
github.com/matrix-org/util v0.0.0-20190711121626-527ce5ddefc7/go.mod h1:vVQlW/emklohkZnOPwD3LrZUBqdfsbiyO3p1lNV8F6U=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54=
github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-shellwords v1.0.10 h1:Y7Xqm8piKOO3v10Thp7Z36h4FYFjt5xB//6XvOrs2Gw=
github.com/mattn/go-shellwords v1.0.10/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c=
@ -79,6 +98,7 @@ github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJK
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mmcdole/gofeed v1.0.0-beta2 h1:CjQ0ADhAwNSb08zknAkGOEYqr8zfZKfrzgk9BxpWP2E=
github.com/mmcdole/gofeed v1.0.0-beta2/go.mod h1:/BF9JneEL2/flujm8XHoxUcghdTV6vvb3xx/vKyChFU=
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
@ -90,9 +110,14 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.8.1-0.20160916180340-5636dc67ae77 h1:YXoHPWLq9PIcMoZg7znMmEzqYHBszdXSemwGQRJoiSk=
github.com/prometheus/client_golang v0.8.1-0.20160916180340-5636dc67ae77/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@ -127,8 +152,14 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y=
github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10=
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@ -138,13 +169,32 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc=
github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls=
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.0.1 h1:WE4RBSZ1x6McVVC8S/Md+Qse8YUv6HRObAx6ke00NY8=
github.com/tidwall/pretty v1.0.1/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/sjson v1.1.1 h1:7h1vk049Jnd5EH9NyzNiEuwYW4b5qgreBbqRC19AS3U=
github.com/tidwall/sjson v1.1.1/go.mod h1:yvVuSnpEQv5cYIrO+AT6kw4QVfd5SDZoGIS7/5+fZFs=
github.com/trivago/tgo v1.0.1 h1:bxatjJIXNIpV18bucU4Uk/LaoxvxuOlp/oowRHyncLQ=
github.com/trivago/tgo v1.0.1/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
github.com/zyedidia/clipboard v0.0.0-20200421031010-7c45b8673834/go.mod h1:zykFnZUXX0ErxqvYLUFEq7QDJKId8rmh2FgD0/Y8cjA=
github.com/zyedidia/poller v1.0.1/go.mod h1:vZXJOHGDcuK08GXhF6IAY0ZFd2WcgOR5DOTp84Uk5eE=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
golang.org/dl v0.0.0-20200601221412-a954fa24b3e5 h1:LrG45X3Uq6Sb+SuDP5WXq1jUhjYbqOyqw92r+E8Q7n0=
golang.org/dl v0.0.0-20200601221412-a954fa24b3e5/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -157,6 +207,8 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c h1:zJ0mtu4jCalhKg6Oaukv6iIkb+cOvDrajDH9DH46Q4M=
golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -165,13 +217,21 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82 h1:ywK/j/KkyTHcdyYSZNXGjMwgmDSfjglYZ3vStQ/gSCU=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200311090712-aafaee8bce8c h1:9WR4YuzLDuQMqEmLQrG0DiMmE2/HvX1dlrujzjmNVFg=
golang.org/x/tools v0.0.0-20200311090712-aafaee8bce8c/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
@ -182,10 +242,20 @@ google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO50
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/toast.v1 v1.0.0-20180812000517-0a84660828b2/go.mod h1:s1Sn2yZos05Qfs7NKt867Xe18emOmtsO3eAKbDaon0o=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
maunium.net/go/gomuks v0.1.0 h1:Ra0But5Cr3UfCSzd456x8MzV6jXLisZG0gW7RZY1xnk=
maunium.net/go/gomuks v0.1.0/go.mod h1:UWB7mC4OcKINQ2+ygalRSsSvnROoiwdSxdVLrWTeco4=
maunium.net/go/maulogger/v2 v2.1.1/go.mod h1:TYWy7wKwz/tIXTpsx8G3mZseIRiC5DoMxSZazOHy68A=
maunium.net/go/mautrix v0.4.2/go.mod h1:8Y+NqmROJyWYvvP4yPfX9tLM59VCfgE/kcQ0SeX68ho=
maunium.net/go/mautrix v0.4.7 h1:jpclbeGcuiHPIWZFZhQJoxgZKP9f+9OLBPtcDNMFV/o=
maunium.net/go/mautrix v0.4.7/go.mod h1:8Y+NqmROJyWYvvP4yPfX9tLM59VCfgE/kcQ0SeX68ho=
maunium.net/go/mauview v0.1.1/go.mod h1:3QBUiuLct9moP1LgDhCGIg0Ovxn38Bd2sGndnUOuj4o=
maunium.net/go/tcell v0.2.0/go.mod h1:9Apcb3lNNS6C6lCqKT9UFp7BTRzHXfWE+/tgufsAMho=

3
goneb.go

@ -19,13 +19,16 @@ import (
"github.com/matrix-org/go-neb/polling"
_ "github.com/matrix-org/go-neb/realms/github"
_ "github.com/matrix-org/go-neb/realms/jira"
_ "github.com/matrix-org/go-neb/services/alertmanager"
_ "github.com/matrix-org/go-neb/services/echo"
_ "github.com/matrix-org/go-neb/services/giphy"
_ "github.com/matrix-org/go-neb/services/github"
_ "github.com/matrix-org/go-neb/services/google"
_ "github.com/matrix-org/go-neb/services/guggy"
_ "github.com/matrix-org/go-neb/services/imgur"
_ "github.com/matrix-org/go-neb/services/jira"
_ "github.com/matrix-org/go-neb/services/rssbot"
_ "github.com/matrix-org/go-neb/services/slackapi"

11
matrix/matrix.go

@ -5,21 +5,22 @@ import (
"github.com/matrix-org/go-neb/api"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/id"
)
// NEBStore implements the gomatrix.Storer interface.
// NEBStore implements the mautrix.Storer interface.
//
// It persists the next batch token in the database, and includes a ClientConfig for the client.
type NEBStore struct {
gomatrix.InMemoryStore
mautrix.InMemoryStore
Database database.Storer
ClientConfig api.ClientConfig
}
// SaveNextBatch saves to the database.
func (s *NEBStore) SaveNextBatch(userID, nextBatch string) {
func (s *NEBStore) SaveNextBatch(userID id.UserID, nextBatch string) {
if err := s.Database.UpdateNextBatch(userID, nextBatch); err != nil {
log.WithFields(log.Fields{
log.ErrorKey: err,
@ -30,7 +31,7 @@ func (s *NEBStore) SaveNextBatch(userID, nextBatch string) {
}
// LoadNextBatch loads from the database.
func (s *NEBStore) LoadNextBatch(userID string) string {
func (s *NEBStore) LoadNextBatch(userID id.UserID) string {
token, err := s.Database.LoadNextBatch(userID)
if err != nil {
log.WithFields(log.Fields{

14
realms/github/github.go

@ -2,6 +2,7 @@
package github
import (
"context"
"crypto/rand"
"encoding/hex"
"encoding/json"
@ -14,6 +15,7 @@ import (
"github.com/matrix-org/go-neb/services/github/client"
"github.com/matrix-org/go-neb/types"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix/id"
)
// RealmType of the Github Realm
@ -41,7 +43,7 @@ type Realm struct {
// Session represents an authenticated github session
type Session struct {
id string
userID string
userID id.UserID
realmID string
// AccessToken is the github access token for the user
@ -86,7 +88,7 @@ func (s *Session) Info() interface{} {
}
for {
// 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 {
logger.WithError(err).Print("Failed to query github projects on github.com")
return nil
@ -110,7 +112,7 @@ func (s *Session) Info() interface{} {
}
// UserID returns the user_id who authorised with Github
func (s *Session) UserID() string {
func (s *Session) UserID() id.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...."
// }
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)
if err != nil {
log.WithError(err).Print("Failed to generate state param")
@ -259,7 +261,7 @@ func (r *Realm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {
return
}
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
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{
id: id,
userID: userID,

15
realms/jira/jira.go

@ -19,6 +19,7 @@ import (
"github.com/matrix-org/go-neb/types"
log "github.com/sirupsen/logrus"
"golang.org/x/net/context"
"maunium.net/go/mautrix/id"
)
// RealmType of the JIRA realm
@ -83,7 +84,7 @@ type Realm struct {
// The endpoint is dictated by the realm ID.
type Session struct {
id string // request token
userID string
userID id.UserID
realmID string
// Configuration fields
@ -121,7 +122,7 @@ func (s *Session) Info() interface{} {
}
// UserID returns the ID of the user performing the authentication.
func (s *Session) UserID() string {
func (s *Session) UserID() id.UserID {
return s.userID
}
@ -203,7 +204,7 @@ func (r *Realm) Register() error {
// {
// "URL": "https://jira.somewhere.com/plugins/servlet/oauth/authorize?oauth_token=7yeuierbgweguiegrTbOT"
// }
func (r *Realm) RequestAuthSession(userID string, req json.RawMessage) interface{} {
func (r *Realm) RequestAuthSession(userID id.UserID, req json.RawMessage) interface{} {
logger := log.WithField("jira_url", r.JIRAEndpoint)
// check if they supplied a redirect URL
@ -298,7 +299,7 @@ func (r *Realm) OnReceiveRedirect(w http.ResponseWriter, req *http.Request) {
}
// AuthSession returns a JIRASession with the given parameters
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{
id: id,
userID: userID,
@ -310,7 +311,7 @@ func (r *Realm) AuthSession(id, userID, realmID string) types.AuthSession {
// An authenticated client for userID will be used if one exists, else an
// unauthenticated client will be used, which may not be able to see the complete list
// of projects.
func (r *Realm) ProjectKeyExists(userID, projectKey string) (bool, error) {
func (r *Realm) ProjectKeyExists(userID id.UserID, projectKey string) (bool, error) {
cli, err := r.JIRAClient(userID, true)
if err != nil {
return false, err
@ -344,7 +345,7 @@ func (r *Realm) ProjectKeyExists(userID, projectKey string) (bool, error) {
// JIRAClient returns an authenticated jira.Client for the given userID. Returns an unauthenticated
// client if allowUnauth is true and no authenticated session is found, else returns an error.
func (r *Realm) JIRAClient(userID string, allowUnauth bool) (*jira.Client, error) {
func (r *Realm) JIRAClient(userID id.UserID, allowUnauth bool) (*jira.Client, error) {
// Check if user has an auth session.
session, err := database.GetServiceDB().LoadAuthSessionByUser(r.id, userID)
if err != nil {
@ -367,7 +368,7 @@ func (r *Realm) JIRAClient(userID string, allowUnauth bool) (*jira.Client, error
// make an unauthenticated client
return jira.NewClient(nil, r.JIRAEndpoint)
}
return nil, errors.New("No authenticated session found for " + userID)
return nil, errors.New("No authenticated session found for " + userID.String())
}
// make an authenticated client
auth := r.oauth1Config(r.JIRAEndpoint)

37
services/alertmanager/alertmanager.go

@ -5,14 +5,17 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
html "html/template"
"net/http"
"strings"
text "text/template"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/types"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Alertmanager service.
@ -46,10 +49,10 @@ type Service struct {
// The URL which should be added to alertmanagers config - Populated by Go-NEB after Service registration.
WebhookURL string `json:"webhook_url"`
// A map of matrix rooms to templates
Rooms map[string]struct {
TextTemplate string `json:"text_template"`
HTMLTemplate string `json:"html_template"`
MsgType string `json:"msg_type"`
Rooms map[id.RoomID]struct {
TextTemplate string `json:"text_template"`
HTMLTemplate string `json:"html_template"`
MsgType mevt.MessageType `json:"msg_type"`
} `json:"rooms"`
}
@ -75,7 +78,7 @@ type WebhookNotification struct {
}
// OnReceiveWebhook receives requests from Alertmanager and sends requests to Matrix as a result.
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) {
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *mautrix.Client) {
decoder := json.NewDecoder(req.Body)
var notif WebhookNotification
if err := decoder.Decode(&notif); err != nil {
@ -115,14 +118,14 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
w.WriteHeader(500)
return
}
msg = gomatrix.HTMLMessage{
msg = mevt.MessageEventContent{
Body: bodyBuffer.String(),
MsgType: templates.MsgType,
Format: "org.matrix.custom.html",
Format: mevt.FormatHTML,
FormattedBody: formattedBodyBuffer.String(),
}
} else {
msg = gomatrix.TextMessage{
msg = mevt.MessageEventContent{
Body: bodyBuffer.String(),
MsgType: templates.MsgType,
}
@ -132,7 +135,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
"message": msg,
"room_id": roomID,
}).Print("Sending Alertmanager notification to room")
if _, e := cli.SendMessageEvent(roomID, "m.room.message", msg); e != nil {
if _, e := cli.SendMessageEvent(roomID, mevt.EventMessage, msg); e != nil {
log.WithError(e).WithField("room_id", roomID).Print(
"Failed to send Alertmanager notification to room.")
}
@ -141,7 +144,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
}
// Register makes sure the Config information supplied is valid.
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error {
func (s *Service) Register(oldService types.Service, client *mautrix.Client) error {
s.WebhookURL = s.webhookEndpointURL
for _, templates := range s.Rooms {
// validate that we have at least a plain text template
@ -188,9 +191,9 @@ func (s *Service) PostRegister(oldService types.Service) {
}
}
func (s *Service) joinRooms(client *gomatrix.Client) {
func (s *Service) joinRooms(client *mautrix.Client) {
for roomID := range s.Rooms {
if _, err := client.JoinRoom(roomID, "", nil); err != nil {
if _, err := client.JoinRoom(roomID.String(), "", nil); err != nil {
log.WithFields(log.Fields{
log.ErrorKey: err,
"room_id": roomID,
@ -201,7 +204,7 @@ func (s *Service) joinRooms(client *gomatrix.Client) {
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
webhookEndpointURL: webhookEndpointURL,

18
services/alertmanager/alertmanager_test.go

@ -4,10 +4,6 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"io/ioutil"
"net/http"
"net/http/httptest"
@ -15,13 +11,19 @@ import (
"regexp"
"strings"
"testing"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
)
func TestNotify(t *testing.T) {
database.SetServiceDB(&database.NopStorage{})
// Intercept message sending to Matrix and mock responses
msgs := []gomatrix.HTMLMessage{}
msgs := []mevt.MessageEventContent{}
matrixCli := buildTestClient(&msgs)
// create the service
@ -91,13 +93,13 @@ func TestNotify(t *testing.T) {
}
}
func buildTestClient(msgs *[]gomatrix.HTMLMessage) *gomatrix.Client {
func buildTestClient(msgs *[]mevt.MessageEventContent) *mautrix.Client {
matrixTrans := struct{ testutils.MockTransport }{}
matrixTrans.RT = func(req *http.Request) (*http.Response, error) {
if !strings.Contains(req.URL.String(), "/send/m.room.message") {
return nil, fmt.Errorf("Unhandled URL: %s", req.URL.String())
}
var msg gomatrix.HTMLMessage
var msg mevt.MessageEventContent
if err := json.NewDecoder(req.Body).Decode(&msg); err != nil {
return nil, fmt.Errorf("Failed to decode request JSON: %s", err)
}
@ -107,7 +109,7 @@ func buildTestClient(msgs *[]gomatrix.HTMLMessage) *gomatrix.Client {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"event_id":"$yup:event"}`)),
}, nil
}
matrixCli, _ := gomatrix.NewClient("https://hs", "@neb:hs", "its_a_secret")
matrixCli, _ := mautrix.NewClient("https://hs", "@neb:hs", "its_a_secret")
matrixCli.Client = &http.Client{Transport: matrixTrans}
return matrixCli
}

15
services/echo/echo.go

@ -5,7 +5,9 @@ import (
"strings"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Echo service
@ -19,19 +21,22 @@ type Service struct {
// Commands supported:
// !echo some message
// Responds with a notice of "some message".
func (e *Service) Commands(cli *gomatrix.Client) []types.Command {
func (e *Service) Commands(cli *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
Path: []string{"echo"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
return &gomatrix.TextMessage{"m.notice", strings.Join(args, " ")}, nil
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: strings.Join(args, " "),
}, nil
},
},
}
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

29
services/giphy/giphy.go

@ -10,8 +10,11 @@ import (
"strings"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Giphy service.
@ -58,18 +61,18 @@ type Service struct {
// Commands supported:
// !giphy some search query without quotes
// Responds with a suitable GIF into the same room as the command.
func (s *Service) Commands(client *gomatrix.Client) []types.Command {
func (s *Service) Commands(client *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
Path: []string{"giphy"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGiphy(client, roomID, userID, args)
},
},
}
}
func (s *Service) cmdGiphy(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGiphy(client *mautrix.Client, roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
// only 1 arg which is the text to search for.
query := strings.Join(args, " ")
gifResult, err := s.searchGiphy(query)
@ -90,14 +93,14 @@ func (s *Service) cmdGiphy(client *gomatrix.Client, roomID, userID string, args
return nil, err
}
return gomatrix.ImageMessage{
MsgType: "m.image",
return mevt.MessageEventContent{
MsgType: event.MsgImage,
Body: gifResult.Slug,
URL: resUpload.ContentURI,
Info: gomatrix.ImageInfo{
URL: resUpload.ContentURI.CUString(),
Info: &mevt.FileInfo{
Height: asInt(image.Height),
Width: asInt(image.Width),
Mimetype: "image/gif",
MimeType: "image/gif",
Size: asInt(image.Size),
},
}, nil
@ -130,16 +133,16 @@ func (s *Service) searchGiphy(query string) (*result, error) {
return &search.Data, nil
}
func asInt(strInt string) uint {
u64, err := strconv.ParseUint(strInt, 10, 32)
func asInt(strInt string) int {
i64, err := strconv.ParseInt(strInt, 10, 32)
if err != nil {
return 0 // default to 0 since these are all just hints to the client
}
return uint(u64)
return int(i64)
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

215
services/github/github.go

@ -5,6 +5,7 @@
package github
import (
"context"
"database/sql"
"fmt"
"regexp"
@ -12,15 +13,18 @@ import (
"strings"
"bytes"
"html"
gogithub "github.com/google/go-github/github"
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/matrix"
"github.com/matrix-org/go-neb/realms/github"
"github.com/matrix-org/go-neb/services/github/client"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
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
@ -69,7 +73,7 @@ type Service struct {
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)
if cli == nil {
var r types.AuthRealm
@ -91,14 +95,17 @@ func (s *Service) requireGithubClientFor(userID string) (cli *gogithub.Client, r
const numberGithubSearchSummaries = 3
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)
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, " ")
searchResult, res, err := cli.Search.Issues(query, nil)
searchResult, res, err := cli.Search.Issues(context.Background(), query, nil)
if err != nil {
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 {
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
@ -130,9 +140,9 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa
}
htmlBuffer.WriteString("</ol>")
return &gomatrix.HTMLMessage{
return &mevt.MessageEventContent{
Body: plainBuffer.String(),
MsgType: "m.notice",
MsgType: mevt.MsgNotice,
Format: "org.matrix.custom.html",
FormattedBody: htmlBuffer.String(),
}, nil
@ -140,13 +150,16 @@ func (s *Service) cmdGithubSearch(roomID, userID string, args []string) (interfa
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)
if cli == nil {
return resp, err
}
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:
@ -159,12 +172,16 @@ 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", "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
ownerRepoGroups = ownerRepoRegex.FindStringSubmatch(defaultRepo)
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
@ -187,7 +204,7 @@ func (s *Service) cmdGithubCreate(roomID, userID string, args []string) (interfa
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,
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 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{
@ -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:)`
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)
if cli == nil {
return resp, err
}
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]]
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]
@ -255,7 +278,7 @@ func (s *Service) cmdGithubReact(roomID, userID string, args []string) (interfac
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 {
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 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"`
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)
if cli == nil {
return resp, err
}
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]
@ -294,7 +323,7 @@ func (s *Service) cmdGithubComment(roomID, userID string, args []string) (interf
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,
})
@ -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 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] [...]`
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)
if cli == nil {
return resp, err
}
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 {
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]
@ -328,7 +366,7 @@ func (s *Service) cmdGithubAssign(roomID, userID string, args []string) (interfa
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 {
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 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)
if cli == nil {
return resp, err
}
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]
@ -356,7 +400,7 @@ func (s *Service) githubIssueCloseReopen(roomID, userID string, args []string, s
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,
})
@ -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 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`
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)
}
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)
}
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:
// "[owner/repo]#issue"
// 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)
if len(ownerRepoIssueGroups) != 5 {
resp = &gomatrix.TextMessage{"m.notice", "Usage: " + usage}
resp = &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: " + usage,
}
return
}
@ -400,7 +450,10 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s
var err error
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
}
@ -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
defaultRepo := s.defaultRepo(roomID)
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
}
segs := strings.Split(defaultRepo, "/")
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
}
@ -424,10 +483,10 @@ func (s *Service) getIssueDetailsFor(input, roomID, usage string) (owner, repo s
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)
i, _, err := cli.Issues.Get(owner, repo, issueNum)
i, _, err := cli.Issues.Get(context.Background(), owner, repo, issueNum)
if err != nil {
log.WithError(err).WithFields(log.Fields{
"owner": owner,
@ -437,16 +496,16 @@ func (s *Service) expandIssue(roomID, userID, owner, repo string, issueNum int)
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)
c, _, err := cli.Repositories.GetCommit(owner, repo, sha)
c, _, err := cli.Repositories.GetCommit(context.Background(), owner, repo, sha)
if err != nil {
log.WithError(err).WithFields(log.Fields{
"owner": owner,
@ -487,10 +546,10 @@ func (s *Service) expandCommit(roomID, userID, owner, repo, sha string) interfac
plainBuffer.WriteString(segs[0])
}
return &gomatrix.HTMLMessage{
return &mevt.MessageEventContent{
Body: plainBuffer.String(),
MsgType: "m.notice",
Format: "org.matrix.custom.html",
MsgType: mevt.MsgNotice,
Format: mevt.FormatHTML,
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
// 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.
func (s *Service) Commands(cli *gomatrix.Client) []types.Command {
func (s *Service) Commands(cli *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
{
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)
},
},
types.Command{
{
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)
},
},
types.Command{
{
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)
},
},
types.Command{
{
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)
},
},
types.Command{
{
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)
},
},
types.Command{
{
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)
},
},
types.Command{
{
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)
},
},
types.Command{
{
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,
cmdGithubReactUsage,
cmdGithubCommentUsage,
@ -573,11 +632,11 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command {
// it will also expand strings of the form:
// #12
// 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{
types.Expansion{
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:
// [foo/bar#55 foo bar 55]
// [#55 55]
@ -619,7 +678,7 @@ func (s *Service) Expansions(cli *gomatrix.Client) []types.Expansion {
},
types.Expansion{
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:
// [foo/bar@a123 foo bar 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.
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 == "" {
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.
func (s *Service) defaultRepo(roomID string) string {
func (s *Service) defaultRepo(roomID id.RoomID) string {
logger := log.WithFields(log.Fields{
"room_id": roomID,
"bot_user_id": s.ServiceUserID(),
@ -707,7 +766,7 @@ func (s *Service) defaultRepo(roomID string) string {
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)
if err != nil {
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)
if err != nil {
return "", err
@ -750,7 +809,7 @@ func getTokenForUser(realmID, userID string) (string, error) {
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

29
services/github/github_webhook.go

@ -1,6 +1,7 @@
package github
import (
"context"
"fmt"
"net/http"
"sort"
@ -11,8 +12,10 @@ import (
"github.com/matrix-org/go-neb/services/github/client"
"github.com/matrix-org/go-neb/services/github/webhook"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
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.
@ -45,12 +48,12 @@ type WebhookService struct {
types.DefaultService
webhookEndpointURL string
// 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 Github credentials of the ClientUserID.
RealmID string
// 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.
Repos map[string]struct { // owner/repo => { events: ["push","issue","pull_request"] }
// 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
// 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)
if err != nil {
w.WriteHeader(err.Code)
@ -108,7 +111,7 @@ func (s *WebhookService) OnReceiveWebhook(w http.ResponseWriter, req *http.Reque
"message": msg,
"room_id": roomID,
}).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(
"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
// 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 == "" {
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 {
if _, err := client.JoinRoom(roomID, "", nil); err != nil {
if _, err := client.JoinRoom(roomID.String(), "", nil); err != nil {
// TODO: Leave the rooms we successfully joined?
return err
}
@ -300,7 +303,7 @@ func (s *WebhookService) createHook(cli *gogithub.Client, ownerRepo string) erro
cfg["secret"] = s.SecretToken
}
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,
Config: cfg,
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
// 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 {
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)
}
_, err = cli.Repositories.DeleteHook(owner, repo, *hook.ID)
_, err = cli.Repositories.DeleteHook(context.Background(), owner, repo, *hook.ID)
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)
if err != nil {
log.WithFields(log.Fields{
@ -462,7 +465,7 @@ func (s *WebhookService) loadRealm() (types.AuthRealm, error) {
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, WebhookServiceType),
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/testutils"
"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"
@ -22,13 +23,13 @@ func TestGithubWebhook(t *testing.T) {
database.SetServiceDB(&database.NopStorage{})
// Intercept message sending to Matrix and mock responses
msgs := []gomatrix.TextMessage{}
msgs := []mevt.MessageEventContent{}
matrixTrans := struct{ testutils.MockTransport }{}
matrixTrans.RT = func(req *http.Request) (*http.Response, error) {
if !strings.Contains(req.URL.String(), "/send/m.room.message") {
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 {
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"}`)),
}, 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}
// create the service

7
services/github/webhook/webhook.go

@ -12,16 +12,17 @@ import (
"strings"
"github.com/google/go-github/github"
"github.com/matrix-org/gomatrix"
"github.com/matrix-org/go-neb/services/utils"
"github.com/matrix-org/util"
log "github.com/sirupsen/logrus"
mevt "maunium.net/go/mautrix/event"
)
// OnReceiveRequest processes incoming github webhook requests and returns a
// matrix message to send, along with parsed repo information.
// The secretToken, if supplied, will be used to verify the request is from
// 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
eventType := r.Header.Get("X-GitHub-Event")
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
}
msg := gomatrix.GetHTMLMessage("m.notice", htmlStr)
msg := utils.StrippedHTMLMessage(mevt.MsgNotice, htmlStr)
return refinedType, repo, &msg, nil
}

48
services/google/google.go

@ -12,8 +12,10 @@ import (
"strings"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Google service
@ -68,23 +70,23 @@ type Service struct {
// Commands supported:
// !google image some_search_query_without_quotes
// Responds with a suitable image into the same room as the command.
func (s *Service) Commands(client *gomatrix.Client) []types.Command {
func (s *Service) Commands(client *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
{
Path: []string{"google", "image"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGoogleImgSearch(client, roomID, userID, args)
},
},
types.Command{
{
Path: []string{"google", "help"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return usageMessage(), nil
},
},
types.Command{
{
Path: []string{"google"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return usageMessage(), nil
},
},
@ -92,12 +94,14 @@ func (s *Service) Commands(client *gomatrix.Client) []types.Command {
}
// usageMessage returns a matrix TextMessage representation of the service usage
func usageMessage() *gomatrix.TextMessage {
return &gomatrix.TextMessage{"m.notice",
`Usage: !google image image_search_text`}
func usageMessage() *mevt.MessageEventContent {
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: `Usage: !google image image_search_text`,
}
}
func (s *Service) cmdGoogleImgSearch(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGoogleImgSearch(client *mautrix.Client, roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
if len(args) < 1 {
return usageMessage(), nil
@ -114,8 +118,8 @@ func (s *Service) cmdGoogleImgSearch(client *gomatrix.Client, roomID, userID str
var imgURL = searchResult.Link
if imgURL == "" {
return gomatrix.TextMessage{
MsgType: "m.notice",
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "No image found!",
}, nil
}
@ -126,14 +130,14 @@ func (s *Service) cmdGoogleImgSearch(client *gomatrix.Client, roomID, userID str
return nil, fmt.Errorf("Failed to upload Google image at URL %s (content type %s) to matrix: %s", imgURL, searchResult.Mime, err.Error())
}
return gomatrix.ImageMessage{
MsgType: "m.image",
return mevt.MessageEventContent{
MsgType: mevt.MsgImage,
Body: querySentence,
URL: resUpload.ContentURI,
Info: gomatrix.ImageInfo{
Height: uint(math.Floor(searchResult.Image.Height)),
Width: uint(math.Floor(searchResult.Image.Width)),
Mimetype: searchResult.Mime,
URL: resUpload.ContentURI.CUString(),
Info: &mevt.FileInfo{
Height: int(math.Floor(searchResult.Image.Height)),
Width: int(math.Floor(searchResult.Image.Width)),
MimeType: searchResult.Mime,
},
}, nil
}
@ -195,7 +199,7 @@ func response2String(res *http.Response) string {
// Initialise the service
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

4
services/google/google_test.go

@ -12,7 +12,7 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
)
// TODO: It would be nice to tabularise this test so we can try failing different combinations of responses to make
@ -101,7 +101,7 @@ func TestCommand(t *testing.T) {
}
return nil, fmt.Errorf("Unknown URL: %s", req.URL.String())
}
matrixCli, _ := gomatrix.NewClient("https://hyrule", "@googlebot:hyrule", "its_a_secret")
matrixCli, _ := mautrix.NewClient("https://hyrule", "@googlebot:hyrule", "its_a_secret")
matrixCli.Client = &http.Client{Transport: matrixTrans}
// Execute the matrix !command

30
services/guggy/guggy.go

@ -11,8 +11,10 @@ import (
"strings"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Guggy service
@ -49,17 +51,17 @@ type Service struct {
// Commands supported:
// !guggy some search query without quotes
// Responds with a suitable GIF into the same room as the command.
func (s *Service) Commands(client *gomatrix.Client) []types.Command {
func (s *Service) Commands(client *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
{
Path: []string{"guggy"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdGuggy(client, roomID, userID, args)
},
},
}
}
func (s *Service) cmdGuggy(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdGuggy(client *mautrix.Client, roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
// only 1 arg which is the text to search for.
querySentence := strings.Join(args, " ")
gifResult, err := s.text2gifGuggy(querySentence)
@ -68,8 +70,8 @@ func (s *Service) cmdGuggy(client *gomatrix.Client, roomID, userID string, args
}
if gifResult.GIF == "" {
return gomatrix.TextMessage{
MsgType: "m.notice",
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "No GIF found!",
}, nil
}
@ -79,14 +81,14 @@ func (s *Service) cmdGuggy(client *gomatrix.Client, roomID, userID string, args
return nil, fmt.Errorf("Failed to upload Guggy image to matrix: %s", err.Error())
}
return gomatrix.ImageMessage{
return mevt.MessageEventContent{
MsgType: "m.image",
Body: querySentence,
URL: resUpload.ContentURI,
Info: gomatrix.ImageInfo{
Height: uint(math.Floor(gifResult.Height)),
Width: uint(math.Floor(gifResult.Width)),
Mimetype: "image/gif",
URL: resUpload.ContentURI.CUString(),
Info: &mevt.FileInfo{
Height: int(math.Floor(gifResult.Height)),
Width: int(math.Floor(gifResult.Width)),
MimeType: "image/gif",
},
}, nil
}
@ -142,7 +144,7 @@ func (s *Service) text2gifGuggy(querySentence string) (*guggyGifResult, error) {
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

4
services/guggy/guggy_test.go

@ -12,7 +12,7 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
)
// TODO: It would be nice to tabularise this test so we can try failing different combinations of responses to make
@ -86,7 +86,7 @@ func TestCommand(t *testing.T) {
}
return nil, fmt.Errorf("Unknown URL: %s", req.URL.String())
}
matrixCli, _ := gomatrix.NewClient("https://hyrule", "@guggybot:hyrule", "its_a_secret")
matrixCli, _ := mautrix.NewClient("https://hyrule", "@guggybot:hyrule", "its_a_secret")
matrixCli.Client = &http.Client{Transport: matrixTrans}
// Execute the matrix !command

50
services/imgur/imgur.go

@ -11,8 +11,10 @@ import (
"strings"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Imgur service
@ -114,17 +116,17 @@ type Service struct {
// Commands supported:
// !imgur some_search_query_without_quotes
// Responds with a suitable image into the same room as the command.
func (s *Service) Commands(client *gomatrix.Client) []types.Command {
func (s *Service) Commands(client *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
{
Path: []string{"imgur", "help"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return usageMessage(), nil
},
},
types.Command{
{
Path: []string{"imgur"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdImgSearch(client, roomID, userID, args)
},
},
@ -132,13 +134,15 @@ func (s *Service) Commands(client *gomatrix.Client) []types.Command {
}
// usageMessage returns a matrix TextMessage representation of the service usage
func usageMessage() *gomatrix.TextMessage {
return &gomatrix.TextMessage{"m.notice",
`Usage: !imgur image_search_text`}
func usageMessage() *mevt.MessageEventContent {
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: !imgur image_search_text",
}
}
// Search Imgur for a relevant image and upload it to matrix
func (s *Service) cmdImgSearch(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdImgSearch(client *mautrix.Client, roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
// Check for query text
if len(args) < 1 {
return usageMessage(), nil
@ -155,8 +159,8 @@ func (s *Service) cmdImgSearch(client *gomatrix.Client, roomID, userID string, a
if searchResultImage != nil {
var imgURL = searchResultImage.Link
if imgURL == "" {
return gomatrix.TextMessage{
MsgType: "m.notice",
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "No image found!",
}, nil
}
@ -168,24 +172,24 @@ func (s *Service) cmdImgSearch(client *gomatrix.Client, roomID, userID string, a
}
// Return image message
return gomatrix.ImageMessage{
return mevt.MessageEventContent{
MsgType: "m.image",
Body: querySentence,
URL: resUpload.ContentURI,
Info: gomatrix.ImageInfo{
Height: uint(searchResultImage.Height),
Width: uint(searchResultImage.Width),
Mimetype: searchResultImage.Type,
URL: resUpload.ContentURI.CUString(),
Info: &mevt.FileInfo{
Height: searchResultImage.Height,
Width: searchResultImage.Width,
MimeType: searchResultImage.Type,
},
}, nil
} else if searchResultAlbum != nil {
return gomatrix.TextMessage{
MsgType: "m.notice",
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Search returned an album - Not currently supported",
}, nil
} else {
return gomatrix.TextMessage{
MsgType: "m.notice",
return mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "No image found!",
}, nil
}
@ -280,7 +284,7 @@ func response2String(res *http.Response) string {
// Initialise the service
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

4
services/imgur/imgur_test.go

@ -12,7 +12,7 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
)
func TestCommand(t *testing.T) {
@ -106,7 +106,7 @@ func TestCommand(t *testing.T) {
}
return nil, fmt.Errorf("Unknown URL: %s", req.URL.String())
}
matrixCli, _ := gomatrix.NewClient("https://hyrule", "@imgurbot:hyrule", "its_a_secret")
matrixCli, _ := mautrix.NewClient("https://hyrule", "@imgurbot:hyrule", "its_a_secret")
matrixCli.Client = &http.Client{Transport: matrixTrans}
// Execute the matrix !command

43
services/jira/jira.go

@ -18,9 +18,12 @@ import (
"github.com/matrix-org/go-neb/realms/jira"
"github.com/matrix-org/go-neb/realms/jira/urls"
"github.com/matrix-org/go-neb/services/jira/webhook"
"github.com/matrix-org/go-neb/services/utils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the JIRA Service
@ -54,9 +57,9 @@ type Service struct {
webhookEndpointURL string
// The user ID to create issues as, or to create/delete webhooks as. This user
// is also used to look up issues for expansions.
ClientUserID string
ClientUserID id.UserID
// A map from Matrix room ID to JIRA realms and project keys.
Rooms map[string]struct {
Rooms map[id.RoomID]struct {
// A map of realm IDs to project keys. The realm IDs determine the JIRA
// endpoint used.
Realms map[string]struct {
@ -73,7 +76,7 @@ type Service struct {
// Register ensures that the given realm IDs are valid JIRA realms and registers webhooks
// with those JIRA endpoints.
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error {
func (s *Service) Register(oldService types.Service, client *mautrix.Client) error {
// We only ever make 1 JIRA webhook which listens for all projects and then filter
// on receive. So we simply need to know if we need to make a webhook or not. We
// need to do this for each unique realm.
@ -94,7 +97,7 @@ func (s *Service) Register(oldService types.Service, client *gomatrix.Client) er
return nil
}
func (s *Service) cmdJiraCreate(roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdJiraCreate(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
// E.g jira create PROJ "Issue title" "Issue desc"
if len(args) <= 1 {
return nil, errors.New("Missing project key (e.g 'ABC') and/or title")
@ -164,13 +167,13 @@ func (s *Service) cmdJiraCreate(roomID, userID string, args []string) (interface
return nil, fmt.Errorf("Failed to create issue: JIRA returned %d", res.StatusCode)
}
return &gomatrix.TextMessage{
"m.notice",
fmt.Sprintf("Created issue: %sbrowse/%s", r.JIRAEndpoint, i.Key),
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: fmt.Sprintf("Created issue: %sbrowse/%s", r.JIRAEndpoint, i.Key),
}, nil
}
func (s *Service) expandIssue(roomID, userID string, issueKeyGroups []string) interface{} {
func (s *Service) expandIssue(roomID id.RoomID, userID id.UserID, issueKeyGroups []string) interface{} {
// issueKeyGroups => ["SYN-123", "SYN", "123"]
if len(issueKeyGroups) != 3 {
log.WithField("groups", issueKeyGroups).Error("Bad number of groups")
@ -221,8 +224,8 @@ func (s *Service) expandIssue(roomID, userID string, issueKeyGroups []string) in
logger.WithError(err).Print("Failed to GET issue")
return err
}
return gomatrix.GetHTMLMessage(
"m.notice",
return utils.StrippedHTMLMessage(
mevt.MsgNotice,
fmt.Sprintf(
"%sbrowse/%s : %s",
jrealm.JIRAEndpoint, issueKey, htmlSummaryForIssue(issue),
@ -239,11 +242,11 @@ func (s *Service) expandIssue(roomID, userID string, issueKeyGroups []string) in
// same project key, which project is chosen is undefined. If there
// is no JIRA account linked to the Matrix user ID, it will return a Starter Link
// if there is a known public project with that project key.
func (s *Service) Commands(cli *gomatrix.Client) []types.Command {
func (s *Service) Commands(cli *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
Path: []string{"jira", "create"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdJiraCreate(roomID, userID, args)
},
},
@ -256,11 +259,11 @@ func (s *Service) Commands(cli *gomatrix.Client) []types.Command {
// to map the project key to a realm, and subsequently the JIRA endpoint to hit.
// If there are multiple projects with the same project key in the Service Config, one will
// be chosen arbitrarily.
func (s *Service) Expansions(cli *gomatrix.Client) []types.Expansion {
func (s *Service) Expansions(cli *mautrix.Client) []types.Expansion {
return []types.Expansion{
types.Expansion{
Regexp: issueKeyRegex,
Expand: func(roomID, userID string, issueKeyGroups []string) interface{} {
Expand: func(roomID id.RoomID, userID id.UserID, issueKeyGroups []string) interface{} {
return s.expandIssue(roomID, userID, issueKeyGroups)
},
},
@ -268,7 +271,7 @@ func (s *Service) Expansions(cli *gomatrix.Client) []types.Expansion {
}
// OnReceiveWebhook receives requests from JIRA and possibly sends requests to Matrix as a result.
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) {
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *mautrix.Client) {
eventProjectKey, event, httpErr := webhook.OnReceiveRequest(req)
if httpErr != nil {
log.Print("Failed to handle JIRA webhook")
@ -297,7 +300,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
continue
}
_, msgErr := cli.SendMessageEvent(
roomID, "m.room.message", gomatrix.GetHTMLMessage("m.notice", htmlText),
roomID, mevt.EventMessage, utils.StrippedHTMLMessage(mevt.MsgNotice, htmlText),
)
if msgErr != nil {
log.WithFields(log.Fields{
@ -312,7 +315,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
w.WriteHeader(200)
}
func (s *Service) realmIDForProject(roomID, projectKey string) string {
func (s *Service) realmIDForProject(roomID id.RoomID, projectKey string) string {
// TODO: Multiple realms with the same pkey will be randomly chosen.
for r, realmConfig := range s.Rooms[roomID].Realms {
for pkey, projectConfig := range realmConfig.Projects {
@ -324,7 +327,7 @@ func (s *Service) realmIDForProject(roomID, projectKey string) string {
return ""
}
func (s *Service) projectToRealm(userID, pkey string) (*jira.Realm, error) {
func (s *Service) projectToRealm(userID id.UserID, pkey string) (*jira.Realm, error) {
// We don't know which JIRA installation this project maps to, so:
// - Get all known JIRA realms and f.e query their endpoints with the
// given user ID's credentials (so if it is a private project they
@ -447,7 +450,7 @@ func htmlForEvent(whe *webhook.Event, jiraBaseURL string) string {
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
webhookEndpointURL: webhookEndpointURL,

7
services/jira/webhook/webhook.go

@ -11,6 +11,7 @@ import (
"github.com/matrix-org/go-neb/realms/jira"
"github.com/matrix-org/util"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix/id"
)
type jiraWebhook struct {
@ -32,7 +33,7 @@ type Event struct {
}
// RegisterHook checks to see if this user is allowed to track the given projects and then tracks them.
func RegisterHook(jrealm *jira.Realm, projects []string, userID, webhookEndpointURL string) error {
func RegisterHook(jrealm *jira.Realm, projects []string, userID id.UserID, webhookEndpointURL string) error {
// Tracking means that a webhook may need to be created on the remote JIRA installation.
// We need to make sure that the user has permission to do this. If they don't, it may still be okay if
// there is an existing webhook set up for this installation by someone else, *PROVIDED* that the projects
@ -120,7 +121,7 @@ func OnReceiveRequest(req *http.Request) (string, *Event, *util.JSONResponse) {
return projKey, &whe, nil
}
func createWebhook(jrealm *jira.Realm, webhookEndpointURL, userID string) error {
func createWebhook(jrealm *jira.Realm, webhookEndpointURL string, userID id.UserID) error {
cli, err := jrealm.JIRAClient(userID, false)
if err != nil {
return err
@ -182,7 +183,7 @@ func getWebhook(cli *gojira.Client, webhookEndpointURL string) (*jiraWebhook, bo
return nebWH, false, nil
}
func checkProjectsArePublic(jrealm *jira.Realm, projects []string, userID string) error {
func checkProjectsArePublic(jrealm *jira.Realm, projects []string, userID id.UserID) error {
publicCli, err := jrealm.JIRAClient("", true)
if err != nil {
return fmt.Errorf("Cannot create public JIRA client")

28
services/rssbot/rssbot.go

@ -16,10 +16,12 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/polling"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"github.com/mmcdole/gofeed"
"github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the RSS Bot service
@ -81,7 +83,7 @@ type Service struct {
// Optional. The time to wait between polls. If this is less than minPollingIntervalSeconds, it is ignored.
PollIntervalMins int `json:"poll_interval_mins"`
// The list of rooms to send feed updates into. This cannot be empty.
Rooms []string `json:"rooms"`
Rooms []id.RoomID `json:"rooms"`
// True if rss bot is unable to poll this feed. This is populated by Go-NEB. Use /getService to
// retrieve this value.
IsFailing bool `json:"is_failing"`
@ -100,7 +102,7 @@ type Service struct {
}
// Register will check the liveness of each RSS feed given. If all feeds check out okay, no error is returned.
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error {
func (s *Service) Register(oldService types.Service, client *mautrix.Client) error {
if len(s.Feeds) == 0 {
// this is an error UNLESS the old service had some feeds in which case they are deleting us :(
var numOldFeeds int
@ -129,8 +131,8 @@ func (s *Service) Register(oldService types.Service, client *gomatrix.Client) er
return nil
}
func (s *Service) joinRooms(client *gomatrix.Client) {
roomSet := make(map[string]bool)
func (s *Service) joinRooms(client *mautrix.Client) {
roomSet := make(map[id.RoomID]bool)
for _, feedInfo := range s.Feeds {
for _, roomID := range feedInfo.Rooms {
roomSet[roomID] = true
@ -138,7 +140,7 @@ func (s *Service) joinRooms(client *gomatrix.Client) {
}
for roomID := range roomSet {
if _, err := client.JoinRoom(roomID, "", nil); err != nil {
if _, err := client.JoinRoom(roomID.String(), "", nil); err != nil {
log.WithFields(log.Fields{
log.ErrorKey: err,
"room_id": roomID,
@ -173,7 +175,7 @@ func (s *Service) PostRegister(oldService types.Service) {
// - Else if there is a Title field, use it as the GUID.
//
// Returns a timestamp representing when this Service should have OnPoll called again.
func (s *Service) OnPoll(cli *gomatrix.Client) time.Time {
func (s *Service) OnPoll(cli *mautrix.Client) time.Time {
logger := log.WithFields(log.Fields{
"service_id": s.ServiceID(),
"service_type": s.ServiceType(),
@ -406,7 +408,7 @@ func (s *Service) newItems(feedURL string, allItems []*gofeed.Item) (items []gof
return
}
func (s *Service) sendToRooms(cli *gomatrix.Client, feedURL string, feed *gofeed.Feed, item gofeed.Item) error {
func (s *Service) sendToRooms(cli *mautrix.Client, feedURL string, feed *gofeed.Feed, item gofeed.Item) error {
logger := log.WithFields(log.Fields{
"feed_url": feedURL,
"title": item.Title,
@ -414,14 +416,14 @@ func (s *Service) sendToRooms(cli *gomatrix.Client, feedURL string, feed *gofeed
})
logger.Info("Sending new feed item")
for _, roomID := range s.Feeds[feedURL].Rooms {
if _, err := cli.SendMessageEvent(roomID, "m.room.message", itemToHTML(feed, item)); err != nil {
if _, err := cli.SendMessageEvent(roomID, mevt.EventMessage, itemToHTML(feed, item)); err != nil {
logger.WithError(err).WithField("room_id", roomID).Error("Failed to send to room")
}
}
return nil
}
func itemToHTML(feed *gofeed.Feed, item gofeed.Item) gomatrix.HTMLMessage {
func itemToHTML(feed *gofeed.Feed, item gofeed.Item) mevt.MessageEventContent {
// If an item does not have a title, try using the feed's title instead
// Create a new variable instead of mutating that which is passed in
itemTitle := item.Title
@ -442,11 +444,11 @@ func itemToHTML(feed *gofeed.Feed, item gofeed.Item) gomatrix.HTMLMessage {
html.EscapeString(item.Author.Email))
}
}
return gomatrix.HTMLMessage{
return mevt.MessageEventContent{
Body: fmt.Sprintf("%s: %s ( %s )",
html.EscapeString(feed.Title), html.EscapeString(itemTitle), html.EscapeString(item.Link)),
MsgType: "m.notice",
Format: "org.matrix.custom.html",
Format: mevt.FormatHTML,
FormattedBody: fmtBody,
// <strong>FeedTitle</strong>:
// <br>
@ -532,7 +534,7 @@ func init() {
cachingClient = &http.Client{
Transport: userAgentRoundTripper{httpcache.NewTransport(lruCache)},
}
types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service {
types.RegisterService(func(serviceID string, serviceUserID id.UserID, webhookEndpointURL string) types.Service {
r := &Service{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

10
services/rssbot/rssbot_test.go

@ -14,7 +14,9 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
const rssFeedXML = `
@ -65,7 +67,7 @@ func createRSSClient(t *testing.T, feedURL string) *Service {
// Configure the service to force OnPoll to query the RSS feed and attempt to send results
// to the right room.
f := rssbot.Feeds[feedURL]
f.Rooms = []string{"!linksroom:hyrule"}
f.Rooms = []id.RoomID{"!linksroom:hyrule"}
f.NextPollTimestampSecs = time.Now().Unix()
rssbot.Feeds[feedURL] = f
@ -84,7 +86,7 @@ func TestHTMLEntities(t *testing.T) {
matrixTrans.RT = func(req *http.Request) (*http.Response, error) {
if strings.HasPrefix(req.URL.Path, "/_matrix/client/r0/rooms/!linksroom:hyrule/send/m.room.message") {
// Check content body to make sure it is decoded
var msg gomatrix.HTMLMessage
var msg mevt.MessageEventContent
if err := json.NewDecoder(req.Body).Decode(&msg); err != nil {
t.Fatal("Failed to decode request JSON: ", err)
return nil, errors.New("Error handling matrix client test request")
@ -104,7 +106,7 @@ func TestHTMLEntities(t *testing.T) {
}
return nil, errors.New("Unhandled matrix client test request")
}
matrixClient, _ := gomatrix.NewClient("https://hyrule", "@happy_mask_salesman:hyrule", "its_a_secret")
matrixClient, _ := mautrix.NewClient("https://hyrule", "@happy_mask_salesman:hyrule", "its_a_secret")
matrixClient.Client = &http.Client{Transport: matrixTrans}
// Invoke OnPoll to trigger the RSS feed update

4
services/slackapi/message.go

@ -12,9 +12,9 @@ import (
"regexp"
"time"
"github.com/matrix-org/gomatrix"
"github.com/russross/blackfriday"
log "github.com/sirupsen/logrus"
mevt "maunium.net/go/mautrix/event"
)
type slackAttachment struct {
@ -195,7 +195,7 @@ func renderSlackAttachment(attachment *slackAttachment) {
}
}
func slackMessageToHTMLMessage(message slackMessage) (html gomatrix.HTMLMessage, err error) {
func slackMessageToHTMLMessage(message slackMessage) (html mevt.MessageEventContent, err error) {
text := linkifyString(message.Text)
if message.Mrkdwn == nil || *message.Mrkdwn == true {
message.TextRendered = template.HTML(blackfriday.MarkdownBasic([]byte(text)))

22
services/slackapi/slackapi.go

@ -5,8 +5,10 @@ import (
"strings"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Slack API service
@ -26,16 +28,16 @@ type Service struct {
types.DefaultService
webhookEndpointURL string
// The URL which should be given to an outgoing slack webhook - Populated by Go-NEB after Service registration.
WebhookURL string `json:"webhook_url"`
RoomID string `json:"room_id"`
MessageType string `json:"message_type"`
WebhookURL string `json:"webhook_url"`
RoomID id.RoomID `json:"room_id"`
MessageType event.MessageType `json:"message_type"`
}
// OnReceiveWebhook receives requests from a slack outgoing webhook and possibly sends requests
// to Matrix as a result.
//
// This requires that the WebhookURL is given to an outgoing slack webhook (see https://api.slack.com/outgoing-webhooks)
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) {
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *mautrix.Client) {
segments := strings.Split(req.URL.Path, "/")
if len(segments) < 2 {
@ -45,7 +47,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
messageType := s.MessageType
if messageType == "" {
messageType = "m.text"
messageType = event.MsgText
}
roomID := s.RoomID
@ -64,15 +66,15 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
}
htmlMessage.MsgType = messageType
cli.SendMessageEvent(
roomID, "m.room.message", htmlMessage,
roomID, event.EventMessage, htmlMessage,
)
w.WriteHeader(200)
}
// Register joins the configured room and sets the public WebhookURL
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error {
func (s *Service) Register(oldService types.Service, client *mautrix.Client) error {
s.WebhookURL = s.webhookEndpointURL
if _, err := client.JoinRoom(s.RoomID, "", nil); err != nil {
if _, err := client.JoinRoom(s.RoomID.String(), "", nil); err != nil {
log.WithFields(log.Fields{
log.ErrorKey: err,
"room_id": s.RoomID,
@ -83,7 +85,7 @@ func (s *Service) Register(oldService types.Service, client *gomatrix.Client) er
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
webhookEndpointURL: webhookEndpointURL,

20
services/travisci/travisci.go

@ -12,8 +12,10 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Travis-CI service.
@ -54,7 +56,7 @@ type Service struct {
// The URL which should be added to .travis.yml - Populated by Go-NEB after Service registration.
WebhookURL string `json:"webhook_url"`
// A map from Matrix room ID to Github-style owner/repo repositories.
Rooms map[string]struct {
Rooms map[id.RoomID]struct {
// A map of "owner/repo" to configuration information
Repos map[string]struct {
// The template string to use when creating notifications.
@ -178,7 +180,7 @@ func outputForTemplate(travisTmpl string, tmpl map[string]string) (out string) {
// webhooks: http://go-neb-endpoint.com/notifications
//
// See https://docs.travis-ci.com/user/notifications#Webhook-notifications for more information.
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) {
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *mautrix.Client) {
if err := req.ParseForm(); err != nil {
log.WithError(err).Error("Failed to read incoming Travis-CI webhook form")
w.WriteHeader(400)
@ -222,7 +224,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
if ownerRepo != whForRepo {
continue
}
msg := gomatrix.TextMessage{
msg := mevt.MessageEventContent{
Body: outputForTemplate(repoData.Template, tmplData),
MsgType: "m.notice",
}
@ -231,7 +233,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
"message": msg,
"room_id": roomID,
}).Print("Sending Travis-CI notification to room")
if _, e := cli.SendMessageEvent(roomID, "m.room.message", msg); e != nil {
if _, e := cli.SendMessageEvent(roomID, mevt.EventMessage, msg); e != nil {
logger.WithError(e).WithField("room_id", roomID).Print(
"Failed to send Travis-CI notification to room.")
}
@ -241,7 +243,7 @@ func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli
}
// Register makes sure the Config information supplied is valid.
func (s *Service) Register(oldService types.Service, client *gomatrix.Client) error {
func (s *Service) Register(oldService types.Service, client *mautrix.Client) error {
s.WebhookURL = s.webhookEndpointURL
for _, roomData := range s.Rooms {
for repo := range roomData.Repos {
@ -273,9 +275,9 @@ func (s *Service) PostRegister(oldService types.Service) {
}
}
func (s *Service) joinRooms(client *gomatrix.Client) {
func (s *Service) joinRooms(client *mautrix.Client) {
for roomID := range s.Rooms {
if _, err := client.JoinRoom(roomID, "", nil); err != nil {
if _, err := client.JoinRoom(roomID.String(), "", nil); err != nil {
log.WithFields(log.Fields{
log.ErrorKey: err,
"room_id": roomID,
@ -286,7 +288,7 @@ func (s *Service) joinRooms(client *gomatrix.Client) {
}
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
webhookEndpointURL: webhookEndpointURL,

13
services/travisci/travisci_test.go

@ -13,7 +13,8 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
)
const travisOrgPEMPublicKey = (`-----BEGIN PUBLIC KEY-----
@ -114,13 +115,13 @@ func TestTravisCI(t *testing.T) {
httpClient = &http.Client{Transport: travisTransport}
// Intercept message sending to Matrix and mock responses
msgs := []gomatrix.TextMessage{}
msgs := []mevt.MessageEventContent{}
matrixTrans := struct{ testutils.MockTransport }{}
matrixTrans.RT = func(req *http.Request) (*http.Response, error) {
if !strings.Contains(req.URL.String(), "/send/m.room.message") {
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 {
return nil, fmt.Errorf("Failed to decode request JSON: %s", err)
}
@ -130,13 +131,13 @@ func TestTravisCI(t *testing.T) {
Body: ioutil.NopCloser(bytes.NewBufferString(`{"event_id":"$yup:event"}`)),
}, nil
}
matrixCli, _ := gomatrix.NewClient("https://hyrule", "@travisci:hyrule", "its_a_secret")
matrixCli, _ := mautrix.NewClient("https://hyrule", "@travisci:hyrule", "its_a_secret")
matrixCli.Client = &http.Client{Transport: matrixTrans}
// BEGIN running the Travis-CI table tests
// ---------------------------------------
for _, test := range travisTests {
msgs = []gomatrix.TextMessage{} // reset sent messages
msgs = []mevt.MessageEventContent{} // reset sent messages
mockWriter := httptest.NewRecorder()
travis := makeService(t, test.Template)
if travis == nil {
@ -172,7 +173,7 @@ func TestTravisCI(t *testing.T) {
}
}
func assertResponse(t *testing.T, w *httptest.ResponseRecorder, msgs []gomatrix.TextMessage, expectCode int, expectMsgLength int) bool {
func assertResponse(t *testing.T, w *httptest.ResponseRecorder, msgs []mevt.MessageEventContent, expectCode int, expectMsgLength int) bool {
if w.Code != expectCode {
t.Errorf("TestTravisCI OnReceiveWebhook want HTTP code %d, got %d", expectCode, w.Code)
return false

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)
}
}

28
services/wikipedia/wikipedia.go

@ -11,8 +11,10 @@ import (
"github.com/jaytaylor/html2text"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
log "github.com/sirupsen/logrus"
"maunium.net/go/mautrix"
mevt "maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)
// ServiceType of the Wikipedia service
@ -49,11 +51,11 @@ type Service struct {
// Commands supported:
// !wikipedia some_search_query_without_quotes
// Responds with a suitable article extract and link to the referenced page into the same room as the command.
func (s *Service) Commands(client *gomatrix.Client) []types.Command {
func (s *Service) Commands(client *mautrix.Client) []types.Command {
return []types.Command{
types.Command{
{
Path: []string{"wikipedia"},
Command: func(roomID, userID string, args []string) (interface{}, error) {
Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
return s.cmdWikipediaSearch(client, roomID, userID, args)
},
},
@ -61,12 +63,14 @@ func (s *Service) Commands(client *gomatrix.Client) []types.Command {
}
// usageMessage returns a matrix TextMessage representation of the service usage
func usageMessage() *gomatrix.TextMessage {
return &gomatrix.TextMessage{"m.notice",
`Usage: !wikipedia search_text`}
func usageMessage() *mevt.MessageEventContent {
return &mevt.MessageEventContent{
MsgType: mevt.MsgNotice,
Body: "Usage: !wikipedia search_text",
}
}
func (s *Service) cmdWikipediaSearch(client *gomatrix.Client, roomID, userID string, args []string) (interface{}, error) {
func (s *Service) cmdWikipediaSearch(client *mautrix.Client, roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
// Check for query text
if len(args) < 1 {
return usageMessage(), nil
@ -81,7 +85,7 @@ func (s *Service) cmdWikipediaSearch(client *gomatrix.Client, roomID, userID str
// No article extracts
if searchResultPage == nil || searchResultPage.Extract == "" {
return gomatrix.TextMessage{
return mevt.MessageEventContent{
MsgType: "m.notice",
Body: "No results",
}, nil
@ -90,7 +94,7 @@ func (s *Service) cmdWikipediaSearch(client *gomatrix.Client, roomID, userID str
// Convert article HTML to text
extractText, err := html2text.FromString(searchResultPage.Extract)
if err != nil {
return gomatrix.TextMessage{
return mevt.MessageEventContent{
MsgType: "m.notice",
Body: "Failed to convert extract to plain text - " + err.Error(),
}, nil
@ -105,7 +109,7 @@ func (s *Service) cmdWikipediaSearch(client *gomatrix.Client, roomID, userID str
extractText += fmt.Sprintf("\nhttp://en.wikipedia.org/?curid=%d", searchResultPage.PageID)
// Return article extract
return gomatrix.TextMessage{
return mevt.MessageEventContent{
MsgType: "m.notice",
Body: extractText,
}, nil
@ -175,7 +179,7 @@ func response2String(res *http.Response) string {
// Initialise the service
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{
DefaultService: types.NewDefaultService(serviceID, serviceUserID, ServiceType),
}

4
services/wikipedia/wikipedia_test.go

@ -12,7 +12,7 @@ import (
"github.com/matrix-org/go-neb/database"
"github.com/matrix-org/go-neb/testutils"
"github.com/matrix-org/go-neb/types"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
)
// TODO: It would be nice to tabularise this test so we can try failing different combinations of responses to make
@ -83,7 +83,7 @@ func TestCommand(t *testing.T) {
matrixTrans.RT = func(req *http.Request) (*http.Response, error) {
return nil, fmt.Errorf("Unknown URL: %s", req.URL.String())
}
matrixCli, _ := gomatrix.NewClient("https://hyrule", "@wikipediabot:hyrule", "its_a_secret")
matrixCli, _ := mautrix.NewClient("https://hyrule", "@wikipediabot:hyrule", "its_a_secret")
matrixCli.Client = &http.Client{Transport: matrixTrans}
// Execute the matrix !command

6
testutil_test.go

@ -5,6 +5,8 @@ import (
"fmt"
"io/ioutil"
"net/http"
"maunium.net/go/mautrix/id"
)
// newResponse creates a new HTTP response with the given data.
@ -46,8 +48,8 @@ func (rt *matrixTripper) Handle(method, path string, handler func(req *http.Requ
rt.handlers[key] = handler
}
func (rt *matrixTripper) HandlePOSTFilter(userID string) {
rt.Handle("POST", "/_matrix/client/r0/user/"+userID+"/filter",
func (rt *matrixTripper) HandlePOSTFilter(userID id.UserID) {
rt.Handle("POST", "/_matrix/client/r0/user/"+userID.String()+"/filter",
func(req *http.Request) (*http.Response, error) {
return newResponse(200, `{
"filter_id":"abcdef"

6
types/actions.go

@ -3,6 +3,8 @@ package types
import (
"regexp"
"strings"
"maunium.net/go/mautrix/id"
)
// A Command is something that a user invokes by sending a message starting with '!'
@ -13,7 +15,7 @@ type Command struct {
Path []string
Arguments []string
Help string
Command func(roomID, userID string, arguments []string) (content interface{}, err error)
Command func(roomID id.RoomID, userID id.UserID, arguments []string) (content interface{}, err error)
}
// An Expansion is something that actives when the user sends any message
@ -22,7 +24,7 @@ type Command struct {
// the appropriate RFC.
type Expansion struct {
Regexp *regexp.Regexp
Expand func(roomID, userID string, matchingGroups []string) interface{}
Expand func(roomID id.RoomID, userID id.UserID, matchingGroups []string) interface{}
}
// Matches if the arguments start with the path of the command.

8
types/auth.go

@ -5,6 +5,8 @@ import (
"encoding/json"
"errors"
"net/http"
"maunium.net/go/mautrix/id"
)
// AuthRealm represents a place where a user can authenticate themselves.
@ -15,8 +17,8 @@ type AuthRealm interface {
Init() error
Register() error
OnReceiveRedirect(w http.ResponseWriter, req *http.Request)
AuthSession(id, userID, realmID string) AuthSession
RequestAuthSession(userID string, config json.RawMessage) interface{}
AuthSession(id string, userID id.UserID, realmID string) AuthSession
RequestAuthSession(userID id.UserID, config json.RawMessage) interface{}
}
var realmsByType = map[string]func(string, string) AuthRealm{}
@ -49,7 +51,7 @@ func CreateAuthRealm(realmID, realmType string, realmJSON []byte) (AuthRealm, er
// an auth realm.
type AuthSession interface {
ID() string
UserID() string
UserID() id.UserID
RealmID() string
Authenticated() bool
Info() interface{}

41
types/service.go

@ -8,14 +8,15 @@ import (
"strings"
"time"
"github.com/matrix-org/gomatrix"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/id"
)
// BotOptions for a given bot user in a given room
type BotOptions struct {
RoomID string
UserID string
SetByUserID string
RoomID id.RoomID
UserID id.UserID
SetByUserID id.UserID
Options map[string]interface{}
}
@ -23,25 +24,25 @@ type BotOptions struct {
type Poller interface {
// OnPoll is called when the poller should poll. Return the timestamp when you want to be polled again.
// Return 0 to never be polled again.
OnPoll(client *gomatrix.Client) time.Time
OnPoll(client *mautrix.Client) time.Time
}
// A Service is the configuration for a bot service.
type Service interface {
// Return the user ID of this service.
ServiceUserID() string
ServiceUserID() id.UserID
// Return an opaque ID used to identify this service.
ServiceID() string
// Return the type of service. This string MUST NOT change.
ServiceType() string
Commands(cli *gomatrix.Client) []Command
Expansions(cli *gomatrix.Client) []Expansion
OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client)
Commands(cli *mautrix.Client) []Command
Expansions(cli *mautrix.Client) []Expansion
OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *mautrix.Client)
// A lifecycle function which is invoked when the service is being registered. The old service, if one exists, is provided,
// along with a Client instance for ServiceUserID(). If this function returns an error, the service will not be registered
// or persisted to the database, and the user's request will fail. This can be useful if you depend on external factors
// such as registering webhooks.
Register(oldService Service, client *gomatrix.Client) error
Register(oldService Service, client *mautrix.Client) error
// A lifecycle function which is invoked after the service has been successfully registered and persisted to the database.
// This function is invoked within the critical section for configuring services, guaranteeing that there will not be
// concurrent modifications to this service whilst this function executes. This lifecycle hook should be used to clean
@ -52,12 +53,12 @@ type Service interface {
// DefaultService NO-OPs the implementation of optional Service interface methods. Feel free to override them.
type DefaultService struct {
id string
serviceUserID string
serviceUserID id.UserID
serviceType string
}
// NewDefaultService creates a new service with implementations for ServiceID(), ServiceType() and ServiceUserID()
func NewDefaultService(serviceID, serviceUserID, serviceType string) DefaultService {
func NewDefaultService(serviceID string, serviceUserID id.UserID, serviceType string) DefaultService {
return DefaultService{serviceID, serviceUserID, serviceType}
}
@ -70,7 +71,7 @@ func (s *DefaultService) ServiceID() string {
// ServiceUserID returns the user ID that the service sends events as. In order for this to return the
// service user ID, DefaultService MUST have been initialised by NewDefaultService, the zero-initialiser
// is NOT enough.
func (s *DefaultService) ServiceUserID() string {
func (s *DefaultService) ServiceUserID() id.UserID {
return s.serviceUserID
}
@ -82,23 +83,23 @@ func (s *DefaultService) ServiceType() string {
}
// Commands returns no commands.
func (s *DefaultService) Commands(cli *gomatrix.Client) []Command {
func (s *DefaultService) Commands(cli *mautrix.Client) []Command {
return []Command{}
}
// Expansions returns no expansions.
func (s *DefaultService) Expansions(cli *gomatrix.Client) []Expansion {
func (s *DefaultService) Expansions(cli *mautrix.Client) []Expansion {
return []Expansion{}
}
// Register does nothing and returns no error.
func (s *DefaultService) Register(oldService Service, client *gomatrix.Client) error { return nil }
func (s *DefaultService) Register(oldService Service, client *mautrix.Client) error { return nil }
// PostRegister does nothing.
func (s *DefaultService) PostRegister(oldService Service) {}
// OnReceiveWebhook does nothing but 200 OK the request.
func (s *DefaultService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) {
func (s *DefaultService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *mautrix.Client) {
w.WriteHeader(200) // Do nothing
}
@ -120,11 +121,11 @@ func BaseURL(u string) error {
return nil
}
var servicesByType = map[string]func(string, string, string) Service{}
var servicesByType = map[string]func(string, id.UserID, string) Service{}
var serviceTypesWhichPoll = map[string]bool{}
// RegisterService registers a factory for creating Service instances.
func RegisterService(factory func(string, string, string) Service) {
func RegisterService(factory func(string, id.UserID, string) Service) {
s := factory("", "", "")
servicesByType[s.ServiceType()] = factory
@ -143,7 +144,7 @@ func PollingServiceTypes() (types []string) {
// CreateService creates a Service of the given type and serviceID.
// Returns an error if the Service couldn't be created.
func CreateService(serviceID, serviceType, serviceUserID string, serviceJSON []byte) (Service, error) {
func CreateService(serviceID, serviceType string, serviceUserID id.UserID, serviceJSON []byte) (Service, error) {
f := servicesByType[serviceType]
if f == nil {
return nil, errors.New("Unknown service type: " + serviceType)

Loading…
Cancel
Save