Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(service): Add matrix service #410

Merged
merged 9 commits into from
Oct 4, 2022
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Yes, please! Contributions of all kinds are very welcome! Feel free to check our
| [DingTalk](https://www.dingtalk.com) | [service/dinding](service/dingding) | [blinkbean/dingtalk](https://github.com/blinkbean/dingtalk) |
| [Discord](https://discord.com) | [service/discord](service/discord) | [bwmarrin/discordgo](https://github.com/bwmarrin/discordgo) |
| [Email](https://wikipedia.org/wiki/Email) | [service/mail](service/mail) | [jordan-wright/email](https://github.com/jordan-wright/email) |
| [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [service/fcm](service/fcm) | [appleboy/go-fcm](https://github.com/appleboy/go-fcm) |
| [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) | [service/fcm](service/fcm) | [appleboy/go-fcm](https://github.com/appleboy/go-fcm) |
| [Lark](https://www.larksuite.com/) | [service/lark](service/lark) | [go-lark/lark](https://github.com/go-lark/lark) |
| [Line](https://line.me) | [service/line](service/line) | [line/line-bot-sdk-go](https://github.com/line/line-bot-sdk-go) |
| [Line Notify](https://notify-bot.line.me) | [service/line](service/line) | [utahta/go-linenotify](https://github.com/utahta/go-linenotify) |
Expand All @@ -103,6 +103,7 @@ Yes, please! Contributions of all kinds are very welcome! Feel free to check our
| [Twitter](https://twitter.com) | [service/twitter](service/twitter) | [dghubble/go-twitter](https://github.com/dghubble/go-twitter) |
| [WeChat](https://www.wechat.com) | [service/wechat](service/wechat) | [silenceper/wechat](https://github.com/silenceper/wechat) |
| [WhatsApp](https://www.whatsapp.com) | [service/whatsapp](service/whatsapp) | [Rhymen/go-whatsapp](https://github.com/Rhymen/go-whatsapp) |
| [Matrix](https://www.matrix.org) | [service/matrix](service/matrix) | [mautrix/go](https://github.com/mautrix/go) |


## Special Thanks <a id="special_thanks"></a>
Expand Down
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -79,17 +79,19 @@ require (
github.com/spf13/cast v1.5.0 // indirect
github.com/stretchr/objx v0.4.0 // indirect
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
github.com/tidwall/gjson v1.14.1 // indirect
github.com/tidwall/gjson v1.14.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect
github.com/ttacon/libphonenumber v1.2.1 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b // indirect
golang.org/x/crypto v0.0.0-20220926161630-eccd6366d1be // indirect
golang.org/x/net v0.0.0-20220930213112-107f3e3c3b0b // indirect
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c // indirect
golang.org/x/sys v0.0.0-20220803195053-6e608f9ce704 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
maunium.net/go/mautrix v0.12.1 // indirect
)
39 changes: 39 additions & 0 deletions go.sum

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions service/matrix/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Matrix

[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/nikoksr/notify/service/matrix)

## Prerequisites

You will need to following information to be able to send messages to Matrix.

- Home server url
- User ID
- AccessToken
- Room ID

## Usage

In the current implementation, using this service requires 2 steps:

1. Provide the necessary credentials explicitly
2. Use the Send message to send a message to the specified room.

```go
package main

import (
"context"
"log"

"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/matrix"
)

func main() {
matrixSvc, err := matrix.New("user-id", "room-id", "home-server", "access-token")
if err != nil {
log.Fatalf("matrix.New() failed: %s", err.Error())
}

notifier := notify.New()
notifier.UseServices(matrixSvc)

err = notifier.Send(context.Background(), "", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}

log.Println("notification sent")
}
```
33 changes: 33 additions & 0 deletions service/matrix/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
Package matrix provides message notification integration for Matrix.

Usage:

package main

import (
"log"

"github.com/nikoksr/notify"
"github.com/nikoksr/notify/service/matrix"
)

func main() {
matrixSvc, err := matrix.New("fake-user-id", "fake-room-id", "fake-home-server", "fake-access-token")

if err != nil {
log.Fatalf("matrix.New() failed: %s", err.Error())
}

notifier := notify.New()
notifier.UseServices(matrixSvc)

err = notifier.Send(context.Background(), "subject", "message")
if err != nil {
log.Fatalf("notifier.Send() failed: %s", err.Error())
}

log.Println("notification sent")
}
*/
package matrix
65 changes: 65 additions & 0 deletions service/matrix/matrix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package matrix
svaloumas marked this conversation as resolved.
Show resolved Hide resolved

import (
"context"
"errors"

matrix "maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)

//go:generate mockery --name=matrixClient --output=. --case=underscore --inpackage
type matrixClient interface {
SendMessageEvent(roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...matrix.ReqSendEvent) (resp *matrix.RespSendEvent, err error)
}

// Compile time check to ensure that matrix.Client implements the matrixClient interface
var _ matrixClient = new(matrix.Client)

// New returns a new instance of a Matrix notification service.
// For more information about the Matrix api specs:
//
// -> https://spec.matrix.org/v1.2/client-server-api
func New(userID id.UserID, roomID id.RoomID, homeServer, accessToken string) (*Matrix, error) {
client, err := matrix.NewClient(homeServer, userID, accessToken)
if err != nil {
return nil, err
}

s := &Matrix{
client: client,
options: ServiceOptions{
homeServer: homeServer,
accessToken: accessToken,
userID: userID,
roomID: roomID,
},
}
return s, nil
}

// Send takes a message body and sends them to the previously set channel.
// you will need an account, access token and roomID
// see https://matrix.org
func (s *Matrix) Send(ctx context.Context, _, message string) error {
messageBody := createMessage(message)

select {
case <-ctx.Done():
return ctx.Err()
default:
_, err := s.client.SendMessageEvent(s.options.roomID, event.EventMessage, &messageBody)
if err != nil {
return errors.New("failed to send message to the room using Matrix")
}
}
return nil
}

func createMessage(message string) Message {
return Message{
Body: message,
Msgtype: event.MsgText,
}
}
49 changes: 49 additions & 0 deletions service/matrix/matrix_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package matrix

import (
"context"
"testing"

"github.com/pkg/errors"
"github.com/stretchr/testify/require"
matrix "maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)

func TestMatrix_New(t *testing.T) {
t.Parallel()
assert := require.New(t)
service, err := New("fake-user-id", "fake-home-server", "fake-home-server", "fake-access-token")
assert.Nil(err)
assert.NotNil(service)
assert.Equal(id.UserID("fake-user-id"), service.options.userID)
assert.Equal("fake-home-server", service.options.homeServer)
assert.Equal("fake-access-token", service.options.accessToken)
}

func TestService_Send(t *testing.T) {
arnocornette marked this conversation as resolved.
Show resolved Hide resolved
t.Parallel()
assert := require.New(t)
// Test response
mockClient := newMockMatrixClient(t)
mockClient.
On("SendMessageEvent", id.RoomID("fake-room-id"), event.EventMessage, &Message{Body: "fake-message", Msgtype: event.MsgText}).Return(&matrix.RespSendEvent{}, nil)
service, _ := New("fake-user-id", "fake-room-id", "fake-home-server", "fake-access-token")
service.client = mockClient
err := service.Send(context.Background(), "", "fake-message")
assert.Nil(err)

mockClient.AssertExpectations(t)
svaloumas marked this conversation as resolved.
Show resolved Hide resolved

// Test error on Send
mockClient = newMockMatrixClient(t)
mockClient.
On("SendMessageEvent", id.RoomID("fake-room-id"), event.EventMessage, &Message{Body: "fake-message", Msgtype: event.MsgText}).Return(nil, errors.New("some-error"))

service, _ = New("fake-user-id", "fake-room-id", "fake-home-server", "fake-access-token")
service.client = mockClient
err = service.Send(context.Background(), "", "fake-message")
assert.NotNil(err)
mockClient.AssertExpectations(t)
}
62 changes: 62 additions & 0 deletions service/matrix/mock_matrix_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions service/matrix/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package matrix

import (
"maunium.net/go/mautrix/event"
"maunium.net/go/mautrix/id"
)

// ServiceOptions allow you to configure the Matrix client options.
type ServiceOptions struct {
homeServer string
accessToken string
userID id.UserID
roomID id.RoomID
}

// Message structure that reassembles the SendMessageEvent
type Message struct {
Body string `json:"body"`
Format string `json:"format,omitempty"`
FormattedBody string `json:"formatted_body,omitempty"`
Msgtype event.MessageType `json:"msgtype"`
}

// Matrix struct that holds necessary data to communicate with the Matrix API
type Matrix struct {
client matrixClient
options ServiceOptions
}