diff --git a/.mockery.yaml b/.mockery.yaml
index bc347d0..8951873 100644
--- a/.mockery.yaml
+++ b/.mockery.yaml
@@ -1,6 +1,6 @@
---
all: True
-dir: mocks
+dir: "{{.PackageName}}/mocks"
outpkg: "mocks"
packages:
- github.com/thegeeklab/wp-matrix/plugin:
+ github.com/thegeeklab/wp-matrix/matrix:
diff --git a/Makefile b/Makefile
index 2a5a56b..2e58f75 100644
--- a/Makefile
+++ b/Makefile
@@ -11,13 +11,14 @@ IMPORT := github.com/thegeeklab/$(EXECUTABLE)
GO ?= go
CWD ?= $(shell pwd)
-PACKAGES ?= $(shell go list ./... | grep -Ev 'mocks')
+PACKAGES ?= $(shell go list ./... | grep -Ev '/mocks$$')
SOURCES ?= $(shell find . -name "*.go" -type f)
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@$(GOFUMPT_PACKAGE_VERSION)
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@$(GOLANGCI_LINT_PACKAGE_VERSION)
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
GOTESTSUM_PACKAGE ?= gotest.tools/gotestsum@latest
+MOCKERY_PACKAGE ?= github.com/vektra/mockery/v2@latest
XGO_VERSION := go-1.22.x
XGO_TARGETS ?= linux/amd64,linux/arm-6,linux/arm-7,linux/arm64
@@ -65,6 +66,7 @@ lint: golangci-lint
.PHONY: generate
generate:
$(GO) generate $(PACKAGES)
+ $(GO) run $(MOCKERY_PACKAGE)
.PHONY: test
test:
diff --git a/matrix/api.go b/matrix/api.go
new file mode 100644
index 0000000..b278ea4
--- /dev/null
+++ b/matrix/api.go
@@ -0,0 +1,14 @@
+package matrix
+
+import (
+ "context"
+
+ "maunium.net/go/mautrix"
+ "maunium.net/go/mautrix/event"
+ "maunium.net/go/mautrix/id"
+)
+
+//nolint:lll
+type APIClient interface {
+ SendMessageEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...mautrix.ReqSendEvent) (resp *mautrix.RespSendEvent, err error)
+}
diff --git a/matrix/matrix.go b/matrix/matrix.go
new file mode 100644
index 0000000..4ba5bfc
--- /dev/null
+++ b/matrix/matrix.go
@@ -0,0 +1,89 @@
+package matrix
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/microcosm-cc/bluemonday"
+ "maunium.net/go/mautrix"
+ "maunium.net/go/mautrix/event"
+ "maunium.net/go/mautrix/format"
+ "maunium.net/go/mautrix/id"
+)
+
+type Client struct {
+ client APIClient
+ Message *Message
+}
+
+type Message struct {
+ client APIClient
+ Opt MessageOptions
+}
+
+type MessageOptions struct {
+ RoomID id.RoomID
+ Message string
+ TemplateUnsafe bool
+}
+
+// NewClient creates a new Matrix client with the given parameters and joins the specified room.
+// It authenticates the user if the userID and token are not provided, and returns a Client struct
+// that can be used to send messages to the room.
+func NewClient(ctx context.Context, url, roomID, userID, token, username, password string) (*Client, error) {
+ muid := id.NewUserID(EnsurePrefix("@", userID), url)
+
+ c, err := mautrix.NewClient(url, muid, token)
+ if err != nil {
+ return nil, err
+ }
+
+ if userID == "" || token == "" {
+ _, err := c.Login(
+ ctx,
+ &mautrix.ReqLogin{
+ Type: "m.login.password",
+ Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: username},
+ Password: password,
+ InitialDeviceDisplayName: "Woodpecker CI",
+ StoreCredentials: true,
+ },
+ )
+ if err != nil {
+ return nil, fmt.Errorf("failed to authenticate user: %w", err)
+ }
+ }
+
+ joinResp, err := c.JoinRoom(ctx, EnsurePrefix("!", roomID), "", nil)
+ if err != nil {
+ return nil, fmt.Errorf("failed to join room: %w", err)
+ }
+
+ return &Client{
+ client: c,
+ Message: &Message{
+ client: c,
+ Opt: MessageOptions{
+ RoomID: joinResp.RoomID,
+ },
+ },
+ }, nil
+}
+
+// Send sends a message to the specified room. It sanitizes the message content
+// to remove potentially unsafe HTML.
+func (m *Message) Send(ctx context.Context) error {
+ content := format.RenderMarkdown(m.Opt.Message, true, m.Opt.TemplateUnsafe)
+
+ if content.FormattedBody != "" {
+ content.Body = format.HTMLToMarkdown(bluemonday.UGCPolicy().Sanitize(content.FormattedBody))
+ content.FormattedBody = bluemonday.UGCPolicy().Sanitize(content.FormattedBody)
+ }
+
+ _, err := m.client.SendMessageEvent(ctx, m.Opt.RoomID, event.EventMessage, content)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/plugin/matrix_test.go b/matrix/matrix_test.go
similarity index 85%
rename from plugin/matrix_test.go
rename to matrix/matrix_test.go
index 3f41231..c33d49a 100644
--- a/plugin/matrix_test.go
+++ b/matrix/matrix_test.go
@@ -1,4 +1,4 @@
-package plugin
+package matrix
import (
"context"
@@ -6,21 +6,21 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
- "github.com/thegeeklab/wp-matrix/plugin/mocks"
+ "github.com/thegeeklab/wp-matrix/matrix/mocks"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/event"
)
-func TestMatrixMessageSend(t *testing.T) {
+func TestMessageSend(t *testing.T) {
tests := []struct {
name string
- messageOpt MatrixMessageOpt
+ messageOpt MessageOptions
want event.MessageEventContent
wantErr bool
}{
{
name: "plain text message",
- messageOpt: MatrixMessageOpt{
+ messageOpt: MessageOptions{
RoomID: "test-room",
Message: "hello world",
},
@@ -31,7 +31,7 @@ func TestMatrixMessageSend(t *testing.T) {
},
{
name: "markdown message",
- messageOpt: MatrixMessageOpt{
+ messageOpt: MessageOptions{
RoomID: "test-room",
Message: "**hello world**",
},
@@ -44,7 +44,7 @@ func TestMatrixMessageSend(t *testing.T) {
},
{
name: "html message",
- messageOpt: MatrixMessageOpt{
+ messageOpt: MessageOptions{
RoomID: "test-room",
Message: "hello
world",
TemplateUnsafe: true,
@@ -58,7 +58,7 @@ func TestMatrixMessageSend(t *testing.T) {
},
{
name: "safe html message",
- messageOpt: MatrixMessageOpt{
+ messageOpt: MessageOptions{
RoomID: "test-room",
Message: "hello world",
TemplateUnsafe: false,
@@ -72,7 +72,7 @@ func TestMatrixMessageSend(t *testing.T) {
},
{
name: "unsafe html message",
- messageOpt: MatrixMessageOpt{
+ messageOpt: MessageOptions{
RoomID: "test-room",
Message: "hello world",
TemplateUnsafe: true,
@@ -89,8 +89,8 @@ func TestMatrixMessageSend(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
- mockClient := mocks.NewMockMautrixClient(t)
- m := &MatrixMessage{
+ mockClient := mocks.NewMockAPIClient(t)
+ m := &Message{
Opt: tt.messageOpt,
client: mockClient,
}
diff --git a/plugin/mocks/mock_MautrixClient.go b/matrix/mocks/mock_APIClient.go
similarity index 55%
rename from plugin/mocks/mock_MautrixClient.go
rename to matrix/mocks/mock_APIClient.go
index b23a7f6..8e733d7 100644
--- a/plugin/mocks/mock_MautrixClient.go
+++ b/matrix/mocks/mock_APIClient.go
@@ -1,4 +1,4 @@
-// Code generated by mockery v2.42.1. DO NOT EDIT.
+// Code generated by mockery v2.43.0. DO NOT EDIT.
package mocks
@@ -13,21 +13,21 @@ import (
mock "github.com/stretchr/testify/mock"
)
-// MockMautrixClient is an autogenerated mock type for the MautrixClient type
-type MockMautrixClient struct {
+// MockAPIClient is an autogenerated mock type for the APIClient type
+type MockAPIClient struct {
mock.Mock
}
-type MockMautrixClient_Expecter struct {
+type MockAPIClient_Expecter struct {
mock *mock.Mock
}
-func (_m *MockMautrixClient) EXPECT() *MockMautrixClient_Expecter {
- return &MockMautrixClient_Expecter{mock: &_m.Mock}
+func (_m *MockAPIClient) EXPECT() *MockAPIClient_Expecter {
+ return &MockAPIClient_Expecter{mock: &_m.Mock}
}
// SendMessageEvent provides a mock function with given fields: ctx, roomID, eventType, contentJSON, extra
-func (_m *MockMautrixClient) SendMessageEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...mautrix.ReqSendEvent) (*mautrix.RespSendEvent, error) {
+func (_m *MockAPIClient) SendMessageEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...mautrix.ReqSendEvent) (*mautrix.RespSendEvent, error) {
_va := make([]interface{}, len(extra))
for _i := range extra {
_va[_i] = extra[_i]
@@ -63,8 +63,8 @@ func (_m *MockMautrixClient) SendMessageEvent(ctx context.Context, roomID id.Roo
return r0, r1
}
-// MockMautrixClient_SendMessageEvent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMessageEvent'
-type MockMautrixClient_SendMessageEvent_Call struct {
+// MockAPIClient_SendMessageEvent_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'SendMessageEvent'
+type MockAPIClient_SendMessageEvent_Call struct {
*mock.Call
}
@@ -74,12 +74,12 @@ type MockMautrixClient_SendMessageEvent_Call struct {
// - eventType event.Type
// - contentJSON interface{}
// - extra ...mautrix.ReqSendEvent
-func (_e *MockMautrixClient_Expecter) SendMessageEvent(ctx interface{}, roomID interface{}, eventType interface{}, contentJSON interface{}, extra ...interface{}) *MockMautrixClient_SendMessageEvent_Call {
- return &MockMautrixClient_SendMessageEvent_Call{Call: _e.mock.On("SendMessageEvent",
+func (_e *MockAPIClient_Expecter) SendMessageEvent(ctx interface{}, roomID interface{}, eventType interface{}, contentJSON interface{}, extra ...interface{}) *MockAPIClient_SendMessageEvent_Call {
+ return &MockAPIClient_SendMessageEvent_Call{Call: _e.mock.On("SendMessageEvent",
append([]interface{}{ctx, roomID, eventType, contentJSON}, extra...)...)}
}
-func (_c *MockMautrixClient_SendMessageEvent_Call) Run(run func(ctx context.Context, roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...mautrix.ReqSendEvent)) *MockMautrixClient_SendMessageEvent_Call {
+func (_c *MockAPIClient_SendMessageEvent_Call) Run(run func(ctx context.Context, roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...mautrix.ReqSendEvent)) *MockAPIClient_SendMessageEvent_Call {
_c.Call.Run(func(args mock.Arguments) {
variadicArgs := make([]mautrix.ReqSendEvent, len(args)-4)
for i, a := range args[4:] {
@@ -92,23 +92,23 @@ func (_c *MockMautrixClient_SendMessageEvent_Call) Run(run func(ctx context.Cont
return _c
}
-func (_c *MockMautrixClient_SendMessageEvent_Call) Return(resp *mautrix.RespSendEvent, err error) *MockMautrixClient_SendMessageEvent_Call {
+func (_c *MockAPIClient_SendMessageEvent_Call) Return(resp *mautrix.RespSendEvent, err error) *MockAPIClient_SendMessageEvent_Call {
_c.Call.Return(resp, err)
return _c
}
-func (_c *MockMautrixClient_SendMessageEvent_Call) RunAndReturn(run func(context.Context, id.RoomID, event.Type, interface{}, ...mautrix.ReqSendEvent) (*mautrix.RespSendEvent, error)) *MockMautrixClient_SendMessageEvent_Call {
+func (_c *MockAPIClient_SendMessageEvent_Call) RunAndReturn(run func(context.Context, id.RoomID, event.Type, interface{}, ...mautrix.ReqSendEvent) (*mautrix.RespSendEvent, error)) *MockAPIClient_SendMessageEvent_Call {
_c.Call.Return(run)
return _c
}
-// NewMockMautrixClient creates a new instance of MockMautrixClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
+// NewMockAPIClient creates a new instance of MockAPIClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
// The first argument is typically a *testing.T value.
-func NewMockMautrixClient(t interface {
+func NewMockAPIClient(t interface {
mock.TestingT
Cleanup(func())
-}) *MockMautrixClient {
- mock := &MockMautrixClient{}
+}) *MockAPIClient {
+ mock := &MockAPIClient{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
diff --git a/plugin/util.go b/matrix/util.go
similarity index 95%
rename from plugin/util.go
rename to matrix/util.go
index 9299b3d..63905ce 100644
--- a/plugin/util.go
+++ b/matrix/util.go
@@ -1,4 +1,4 @@
-package plugin
+package matrix
import "strings"
diff --git a/plugin/util_test.go b/matrix/util_test.go
similarity index 98%
rename from plugin/util_test.go
rename to matrix/util_test.go
index be1e7b8..d79d4dc 100644
--- a/plugin/util_test.go
+++ b/matrix/util_test.go
@@ -1,4 +1,4 @@
-package plugin
+package matrix
import "testing"
diff --git a/plugin/impl.go b/plugin/impl.go
index 4080255..7accc74 100644
--- a/plugin/impl.go
+++ b/plugin/impl.go
@@ -12,9 +12,8 @@ import (
"fmt"
"github.com/rs/zerolog/log"
+ "github.com/thegeeklab/wp-matrix/matrix"
"github.com/thegeeklab/wp-plugin-go/v2/template"
- "maunium.net/go/mautrix"
- "maunium.net/go/mautrix/id"
)
var ErrAuthSourceNotSet = errors.New("either username and password or userid and accesstoken are required")
@@ -44,44 +43,26 @@ func (p *Plugin) Validate() error {
// Execute provides the implementation of the plugin.
func (p *Plugin) Execute() error {
- muid := id.NewUserID(EnsurePrefix("@", p.Settings.UserID), p.Settings.Homeserver)
-
- matrix, err := mautrix.NewClient(p.Settings.Homeserver, muid, p.Settings.AccessToken)
- if err != nil {
- return fmt.Errorf("failed to initialize client: %w", err)
- }
-
- if p.Settings.UserID == "" || p.Settings.AccessToken == "" {
- _, err := matrix.Login(
- p.Network.Context,
- &mautrix.ReqLogin{
- Type: "m.login.password",
- Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: p.Settings.Username},
- Password: p.Settings.Password,
- InitialDeviceDisplayName: "Woodpecker CI",
- StoreCredentials: true,
- },
- )
- if err != nil {
- return fmt.Errorf("failed to authenticate user: %w", err)
- }
- }
-
- log.Info().Msg("logged in successfully")
-
- joinResp, err := matrix.JoinRoom(p.Network.Context, EnsurePrefix("!", p.Settings.RoomID), "", nil)
- if err != nil {
- return fmt.Errorf("failed to join room: %w", err)
- }
-
msg, err := p.CreateMessage()
if err != nil {
return fmt.Errorf("failed to create message: %w", err)
}
- client := NewMatrixClient(matrix)
- client.Message.Opt = MatrixMessageOpt{
- RoomID: joinResp.RoomID,
+ client, err := matrix.NewClient(
+ p.Network.Context,
+ p.Settings.Homeserver,
+ p.Settings.RoomID,
+ p.Settings.UserID,
+ p.Settings.AccessToken,
+ p.Settings.Username,
+ p.Settings.Password,
+ )
+ if err != nil {
+ return fmt.Errorf("failed to initialize client: %w", err)
+ }
+
+ client.Message.Opt = matrix.MessageOptions{
+ RoomID: client.Message.Opt.RoomID,
Message: msg,
TemplateUnsafe: p.Settings.TemplateUnsafe,
}
diff --git a/plugin/matrix.go b/plugin/matrix.go
deleted file mode 100644
index 82b3a94..0000000
--- a/plugin/matrix.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package plugin
-
-import (
- "context"
- "fmt"
-
- "github.com/microcosm-cc/bluemonday"
- "maunium.net/go/mautrix"
- "maunium.net/go/mautrix/event"
- "maunium.net/go/mautrix/format"
- "maunium.net/go/mautrix/id"
-)
-
-//nolint:lll
-type MautrixClient interface {
- SendMessageEvent(ctx context.Context, roomID id.RoomID, eventType event.Type, contentJSON interface{}, extra ...mautrix.ReqSendEvent) (resp *mautrix.RespSendEvent, err error)
-}
-
-type MatrixClient struct {
- client MautrixClient
- Message *MatrixMessage
-}
-
-type MatrixMessage struct {
- client MautrixClient
- Opt MatrixMessageOpt
-}
-
-type MatrixMessageOpt struct {
- RoomID id.RoomID
- Message string
- TemplateUnsafe bool
-}
-
-// NewMatrixClient creates a new MatrixClient instance with the provided mautrix.Client.
-func NewMatrixClient(client *mautrix.Client) *MatrixClient {
- return &MatrixClient{
- client: client,
- Message: &MatrixMessage{
- client: client,
- Opt: MatrixMessageOpt{},
- },
- }
-}
-
-// Send sends a message to the specified room. It sanitizes the message content
-// to remove potentially unsafe HTML.
-func (m *MatrixMessage) Send(ctx context.Context) error {
- content := format.RenderMarkdown(m.Opt.Message, true, m.Opt.TemplateUnsafe)
-
- if content.FormattedBody != "" {
- content.Body = format.HTMLToMarkdown(bluemonday.UGCPolicy().Sanitize(content.FormattedBody))
- content.FormattedBody = bluemonday.UGCPolicy().Sanitize(content.FormattedBody)
- }
-
- _, err := m.client.SendMessageEvent(ctx, m.Opt.RoomID, event.EventMessage, content)
- if err != nil {
- return fmt.Errorf("failed to send message: %w", err)
- }
-
- return nil
-}
diff --git a/plugin/plugin.go b/plugin/plugin.go
index 4dbfabf..de5a3a1 100644
--- a/plugin/plugin.go
+++ b/plugin/plugin.go
@@ -13,7 +13,6 @@ import (
"github.com/urfave/cli/v2"
)
-//go:generate mockery
//go:generate go run ../internal/doc/main.go -output=../docs/data/data-raw.yaml
//nolint:lll