mirror of
https://github.com/thegeeklab/wp-matrix.git
synced 2024-11-23 07:00:40 +00:00
refactor: switch to plugin Cmd and add tests (#110)
This commit is contained in:
parent
4aa20c5f0e
commit
117f681b10
6
.mockery.yaml
Normal file
6
.mockery.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
all: True
|
||||||
|
dir: mocks
|
||||||
|
outpkg: "mocks"
|
||||||
|
packages:
|
||||||
|
github.com/thegeeklab/wp-matrix/plugin:
|
9
Makefile
9
Makefile
@ -11,7 +11,7 @@ IMPORT := github.com/thegeeklab/$(EXECUTABLE)
|
|||||||
|
|
||||||
GO ?= go
|
GO ?= go
|
||||||
CWD ?= $(shell pwd)
|
CWD ?= $(shell pwd)
|
||||||
PACKAGES ?= $(shell go list ./...)
|
PACKAGES ?= $(shell go list ./... | grep -Ev 'mocks')
|
||||||
SOURCES ?= $(shell find . -name "*.go" -type f)
|
SOURCES ?= $(shell find . -name "*.go" -type f)
|
||||||
|
|
||||||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@$(GOFUMPT_PACKAGE_VERSION)
|
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@$(GOFUMPT_PACKAGE_VERSION)
|
||||||
@ -19,7 +19,6 @@ GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@$(G
|
|||||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||||
GOTESTSUM_PACKAGE ?= gotest.tools/gotestsum@latest
|
GOTESTSUM_PACKAGE ?= gotest.tools/gotestsum@latest
|
||||||
|
|
||||||
GENERATE ?=
|
|
||||||
XGO_VERSION := go-1.22.x
|
XGO_VERSION := go-1.22.x
|
||||||
XGO_TARGETS ?= linux/amd64,linux/arm-6,linux/arm-7,linux/arm64
|
XGO_TARGETS ?= linux/amd64,linux/arm-6,linux/arm-7,linux/arm64
|
||||||
|
|
||||||
@ -65,11 +64,7 @@ lint: golangci-lint
|
|||||||
|
|
||||||
.PHONY: generate
|
.PHONY: generate
|
||||||
generate:
|
generate:
|
||||||
$(GO) generate $(GENERATE)
|
$(GO) generate $(PACKAGES)
|
||||||
|
|
||||||
.PHONY: generate-docs
|
|
||||||
generate-docs:
|
|
||||||
$(GO) generate ./cmd/$(EXECUTABLE)/flags.go
|
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
test:
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
//go:build generate
|
|
||||||
// +build generate
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"embed"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/thegeeklab/wp-matrix/plugin"
|
|
||||||
"github.com/thegeeklab/wp-plugin-go/docs"
|
|
||||||
wp "github.com/thegeeklab/wp-plugin-go/plugin"
|
|
||||||
wp_template "github.com/thegeeklab/wp-plugin-go/template"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed templates/docs-data.yaml.tmpl
|
|
||||||
var yamlTemplate embed.FS
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
settings := &plugin.Settings{}
|
|
||||||
app := &cli.App{
|
|
||||||
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
|
|
||||||
}
|
|
||||||
|
|
||||||
out, err := toYAML(app)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fi, err := os.Create("../../docs/data/data-raw.yaml")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer fi.Close()
|
|
||||||
if _, err := fi.WriteString(out); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func toYAML(app *cli.App) (string, error) {
|
|
||||||
var w bytes.Buffer
|
|
||||||
|
|
||||||
yamlTmpl, err := template.New("docs").Funcs(wp_template.LoadFuncMap()).ParseFS(yamlTemplate, "templates/docs-data.yaml.tmpl")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(yamlTmpl)
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := yamlTmpl.ExecuteTemplate(&w, "docs-data.yaml.tmpl", docs.GetTemplateData(app)); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return w.String(), nil
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
// Copyright (c) 2020, the Drone Plugins project authors.
|
|
||||||
// Copyright (c) 2021, Robert Kaussow <mail@thegeeklab.de>
|
|
||||||
|
|
||||||
// Use of this source code is governed by an Apache 2.0 license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/thegeeklab/wp-matrix/plugin"
|
|
||||||
"github.com/urfave/cli/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// settingsFlags has the cli.Flags for the plugin.Settings.
|
|
||||||
//
|
|
||||||
//go:generate go run docs.go flags.go
|
|
||||||
func settingsFlags(settings *plugin.Settings, category string) []cli.Flag {
|
|
||||||
return []cli.Flag{
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "username",
|
|
||||||
EnvVars: []string{"PLUGIN_USERNAME", "MATRIX_USERNAME"},
|
|
||||||
Usage: "authentication username",
|
|
||||||
Destination: &settings.Username,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "password",
|
|
||||||
EnvVars: []string{"PLUGIN_PASSWORD", "MATRIX_PASSWORD"},
|
|
||||||
Usage: "authentication password",
|
|
||||||
Destination: &settings.Password,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "userid",
|
|
||||||
EnvVars: []string{"PLUGIN_USER_ID", "PLUGIN_USERID", "MATRIX_USER_ID", "MATRIX_USERID"},
|
|
||||||
Usage: "authentication user ID",
|
|
||||||
Destination: &settings.UserID,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "accesstoken",
|
|
||||||
EnvVars: []string{"PLUGIN_ACCESS_TOKEN", "PLUGIN_ACCESSTOKEN", "MATRIX_ACCESS_TOKEN", "MATRIX_ACCESSTOKEN"},
|
|
||||||
Usage: "authentication access token",
|
|
||||||
Destination: &settings.AccessToken,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "homeserver",
|
|
||||||
EnvVars: []string{"PLUGIN_HOMESERVER", "MATRIX_HOMESERVER"},
|
|
||||||
Usage: "matrix home server url",
|
|
||||||
Value: "https://matrix.org",
|
|
||||||
Destination: &settings.Homeserver,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "roomid",
|
|
||||||
EnvVars: []string{"PLUGIN_ROOMID", "MATRIX_ROOMID"},
|
|
||||||
Usage: "roomid to send messages to",
|
|
||||||
Destination: &settings.RoomID,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
&cli.StringFlag{
|
|
||||||
Name: "template",
|
|
||||||
EnvVars: []string{"PLUGIN_TEMPLATE", "MATRIX_TEMPLATE"},
|
|
||||||
Usage: "golang template for the message",
|
|
||||||
Value: plugin.DefaultMessageTemplate,
|
|
||||||
Destination: &settings.Template,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
&cli.BoolFlag{
|
|
||||||
Name: "template-unsafe",
|
|
||||||
EnvVars: []string{"PLUGIN_TEMPLATE_UNSAFE", "MATRIX_TEMPLATE_UNSAFE"},
|
|
||||||
Usage: "render raw HTML and potentially dangerous links in template",
|
|
||||||
Destination: &settings.TemplateUnsafe,
|
|
||||||
Category: category,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,11 +7,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/thegeeklab/wp-matrix/plugin"
|
"github.com/thegeeklab/wp-matrix/plugin"
|
||||||
|
|
||||||
wp "github.com/thegeeklab/wp-plugin-go/plugin"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint:gochecknoglobals
|
//nolint:gochecknoglobals
|
||||||
@ -21,14 +17,5 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
settings := &plugin.Settings{}
|
plugin.New(nil, BuildVersion, BuildDate).Run()
|
||||||
options := wp.Options{
|
|
||||||
Name: "wp-matrix",
|
|
||||||
Description: "Send messages to a Matrix room",
|
|
||||||
Version: BuildVersion,
|
|
||||||
VersionMetadata: fmt.Sprintf("date=%s", BuildDate),
|
|
||||||
Flags: settingsFlags(settings, wp.FlagsPluginCategory),
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.New(options, settings).Run()
|
|
||||||
}
|
}
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
---
|
|
||||||
{{- if .GlobalArgs }}
|
|
||||||
properties:
|
|
||||||
{{- range $v := .GlobalArgs }}
|
|
||||||
- name: {{ $v.Name }}
|
|
||||||
{{- with $v.Description }}
|
|
||||||
description: |
|
|
||||||
{{ . | ToSentence }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with $v.Type }}
|
|
||||||
type: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with $v.Default }}
|
|
||||||
defaultValue: {{ . }}
|
|
||||||
{{- end }}
|
|
||||||
required: {{ default false $v.Required }}
|
|
||||||
{{ end -}}
|
|
||||||
{{ end -}}
|
|
@ -13,6 +13,20 @@ properties:
|
|||||||
defaultValue: "https://matrix.org"
|
defaultValue: "https://matrix.org"
|
||||||
required: false
|
required: false
|
||||||
|
|
||||||
|
- name: insecure_skip_verify
|
||||||
|
description: |
|
||||||
|
Skip SSL verification.
|
||||||
|
type: bool
|
||||||
|
defaultValue: false
|
||||||
|
required: false
|
||||||
|
|
||||||
|
- name: log_level
|
||||||
|
description: |
|
||||||
|
Plugin log level.
|
||||||
|
type: string
|
||||||
|
defaultValue: "info"
|
||||||
|
required: false
|
||||||
|
|
||||||
- name: password
|
- name: password
|
||||||
description: |
|
description: |
|
||||||
Authentication password.
|
Authentication password.
|
||||||
|
13
go.mod
13
go.mod
@ -5,7 +5,8 @@ go 1.22
|
|||||||
require (
|
require (
|
||||||
github.com/microcosm-cc/bluemonday v1.0.26
|
github.com/microcosm-cc/bluemonday v1.0.26
|
||||||
github.com/rs/zerolog v1.32.0
|
github.com/rs/zerolog v1.32.0
|
||||||
github.com/thegeeklab/wp-plugin-go v1.7.1
|
github.com/stretchr/testify v1.9.0
|
||||||
|
github.com/thegeeklab/wp-plugin-go/v2 v2.3.1
|
||||||
github.com/urfave/cli/v2 v2.27.2
|
github.com/urfave/cli/v2 v2.27.2
|
||||||
maunium.net/go/mautrix v0.18.1
|
maunium.net/go/mautrix v0.18.1
|
||||||
)
|
)
|
||||||
@ -16,6 +17,7 @@ require (
|
|||||||
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
|
||||||
github.com/aymerick/douceur v0.2.0 // indirect
|
github.com/aymerick/douceur v0.2.0 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/google/uuid v1.3.0 // indirect
|
github.com/google/uuid v1.3.0 // indirect
|
||||||
github.com/gorilla/css v1.0.0 // indirect
|
github.com/gorilla/css v1.0.0 // indirect
|
||||||
github.com/huandu/xstrings v1.3.3 // indirect
|
github.com/huandu/xstrings v1.3.3 // indirect
|
||||||
@ -25,9 +27,11 @@ require (
|
|||||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||||
github.com/shopspring/decimal v1.3.1 // indirect
|
github.com/shopspring/decimal v1.3.1 // indirect
|
||||||
github.com/spf13/cast v1.4.1 // indirect
|
github.com/spf13/cast v1.4.1 // indirect
|
||||||
|
github.com/stretchr/objx v0.5.2 // indirect
|
||||||
github.com/tidwall/gjson v1.17.1 // indirect
|
github.com/tidwall/gjson v1.17.1 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.0 // indirect
|
github.com/tidwall/pretty v1.2.0 // indirect
|
||||||
@ -35,9 +39,10 @@ require (
|
|||||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
|
||||||
github.com/yuin/goldmark v1.7.1 // indirect
|
github.com/yuin/goldmark v1.7.1 // indirect
|
||||||
go.mau.fi/util v0.4.2 // indirect
|
go.mau.fi/util v0.4.2 // indirect
|
||||||
golang.org/x/crypto v0.22.0 // indirect
|
golang.org/x/crypto v0.23.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
|
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
|
||||||
golang.org/x/net v0.24.0 // indirect
|
golang.org/x/net v0.25.0 // indirect
|
||||||
golang.org/x/sys v0.19.0 // indirect
|
golang.org/x/sys v0.20.0 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
19
go.sum
19
go.sum
@ -54,12 +54,14 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
|
|||||||
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
|
||||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||||
|
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/thegeeklab/wp-plugin-go v1.7.1 h1:zfR/rfNPuyVhXJu1fsLfp4+Mz2pTf6WwW/mIqw9750I=
|
github.com/thegeeklab/wp-plugin-go/v2 v2.3.1 h1:ARwYgTPZ5iPsmOenmqcCf8TjiEe8wBOHKO7H/Xshe48=
|
||||||
github.com/thegeeklab/wp-plugin-go v1.7.1/go.mod h1:Ixi5plt9tpFGTu6yc/Inm5DcDpp3xPTeohfr86gf2EU=
|
github.com/thegeeklab/wp-plugin-go/v2 v2.3.1/go.mod h1:0t8M8txtEFiaB6RqLX8vLrxkqAo5FT5Hx7dztN592D4=
|
||||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
@ -81,8 +83,8 @@ go.mau.fi/util v0.4.2/go.mod h1:PlAVfUUcPyHPrwnvjkJM9UFcPE7qGPDJqk+Oufa1Gtw=
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
|
||||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
|
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
|
||||||
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
@ -90,8 +92,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
|
||||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -103,8 +105,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
@ -116,6 +118,7 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
|
|||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
42
internal/doc/main.go
Normal file
42
internal/doc/main.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
//go:build generate
|
||||||
|
// +build generate
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/thegeeklab/wp-matrix/plugin"
|
||||||
|
"github.com/thegeeklab/wp-plugin-go/v2/docs"
|
||||||
|
"github.com/thegeeklab/wp-plugin-go/v2/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
tmpl := "https://raw.githubusercontent.com/thegeeklab/woodpecker-plugins/main/templates/docs-data.yaml.tmpl"
|
||||||
|
client := http.Client{
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
p := plugin.New(nil)
|
||||||
|
|
||||||
|
out, err := template.Render(context.Background(), client, tmpl, docs.GetTemplateData(p.App))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFile := flag.String("output", "", "Output file path")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *outputFile == "" {
|
||||||
|
panic("no output file specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.WriteFile(*outputFile, []byte(out), 0o644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
@ -10,15 +10,10 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/microcosm-cc/bluemonday"
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/thegeeklab/wp-plugin-go/template"
|
"github.com/thegeeklab/wp-plugin-go/v2/template"
|
||||||
"maunium.net/go/mautrix"
|
"maunium.net/go/mautrix"
|
||||||
"maunium.net/go/mautrix/event"
|
|
||||||
"maunium.net/go/mautrix/format"
|
|
||||||
"maunium.net/go/mautrix/id"
|
"maunium.net/go/mautrix/id"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -49,15 +44,15 @@ func (p *Plugin) Validate() error {
|
|||||||
|
|
||||||
// Execute provides the implementation of the plugin.
|
// Execute provides the implementation of the plugin.
|
||||||
func (p *Plugin) Execute() error {
|
func (p *Plugin) Execute() error {
|
||||||
muid := id.NewUserID(prepend("@", p.Settings.UserID), p.Settings.Homeserver)
|
muid := id.NewUserID(EnsurePrefix("@", p.Settings.UserID), p.Settings.Homeserver)
|
||||||
|
|
||||||
client, err := mautrix.NewClient(p.Settings.Homeserver, muid, p.Settings.AccessToken)
|
matrix, err := mautrix.NewClient(p.Settings.Homeserver, muid, p.Settings.AccessToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to initialize client: %w", err)
|
return fmt.Errorf("failed to initialize client: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.Settings.UserID == "" || p.Settings.AccessToken == "" {
|
if p.Settings.UserID == "" || p.Settings.AccessToken == "" {
|
||||||
_, err := client.Login(
|
_, err := matrix.Login(
|
||||||
p.Network.Context,
|
p.Network.Context,
|
||||||
&mautrix.ReqLogin{
|
&mautrix.ReqLogin{
|
||||||
Type: "m.login.password",
|
Type: "m.login.password",
|
||||||
@ -74,18 +69,25 @@ func (p *Plugin) Execute() error {
|
|||||||
|
|
||||||
log.Info().Msg("logged in successfully")
|
log.Info().Msg("logged in successfully")
|
||||||
|
|
||||||
joined, err := client.JoinRoom(p.Network.Context, prepend("!", p.Settings.RoomID), "", nil)
|
joinResp, err := matrix.JoinRoom(p.Network.Context, EnsurePrefix("!", p.Settings.RoomID), "", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to join room: %w", err)
|
return fmt.Errorf("failed to join room: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := p.messageContent(p.Network.Context, *p.Network.Client)
|
msg, err := p.CreateMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to render template: %w", err)
|
return fmt.Errorf("failed to create message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := client.SendMessageEvent(p.Network.Context, joined.RoomID, event.EventMessage, content); err != nil {
|
client := NewMatrixClient(matrix)
|
||||||
return fmt.Errorf("failed to submit message: %w", err)
|
client.Message.Opt = MatrixMessageOpt{
|
||||||
|
RoomID: joinResp.RoomID,
|
||||||
|
Message: msg,
|
||||||
|
TemplateUnsafe: p.Settings.TemplateUnsafe,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := client.Message.Send(p.Network.Context); err != nil {
|
||||||
|
return fmt.Errorf("failed to send message: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info().Msg("message sent successfully")
|
log.Info().Msg("message sent successfully")
|
||||||
@ -93,34 +95,7 @@ func (p *Plugin) Execute() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Plugin) messageContent(ctx context.Context, client http.Client) (event.MessageEventContent, error) {
|
// CreateMessage generates a message string based on the plugin's template and metadata.
|
||||||
message, err := template.RenderTrim(ctx, client, p.Settings.Template, p.Metadata)
|
func (p *Plugin) CreateMessage() (string, error) {
|
||||||
if err != nil {
|
return template.RenderTrim(p.Network.Context, *p.Network.Client, p.Settings.Template, p.Metadata)
|
||||||
return event.MessageEventContent{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
content := format.RenderMarkdown(message, true, p.Settings.TemplateUnsafe)
|
|
||||||
|
|
||||||
safeBody := format.HTMLToMarkdown(bluemonday.UGCPolicy().Sanitize(content.FormattedBody))
|
|
||||||
if content.Body != safeBody {
|
|
||||||
content.Body = safeBody
|
|
||||||
}
|
|
||||||
|
|
||||||
if content.FormattedBody != "" {
|
|
||||||
content.FormattedBody = bluemonday.UGCPolicy().Sanitize(content.FormattedBody)
|
|
||||||
}
|
|
||||||
|
|
||||||
return content, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func prepend(prefix, input string) string {
|
|
||||||
if strings.TrimSpace(input) == "" {
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
if strings.HasPrefix(input, prefix) {
|
|
||||||
return input
|
|
||||||
}
|
|
||||||
|
|
||||||
return prefix + input
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
wp "github.com/thegeeklab/wp-plugin-go/plugin"
|
"github.com/stretchr/testify/assert"
|
||||||
|
wp "github.com/thegeeklab/wp-plugin-go/v2/plugin"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_messageContent(t *testing.T) {
|
func Test_messageContent(t *testing.T) {
|
||||||
@ -13,9 +14,7 @@ func Test_messageContent(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
want string
|
want string
|
||||||
unsafe bool
|
|
||||||
template string
|
template string
|
||||||
meta wp.Metadata
|
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "render default template",
|
name: "render default template",
|
||||||
@ -25,29 +24,15 @@ func Test_messageContent(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "render unsafe html template",
|
name: "render unsafe html template",
|
||||||
want: "Status: **success**\nBuild: octocat/demo",
|
want: "Status: **success**\nBuild: octocat/demo",
|
||||||
unsafe: true,
|
template: "Status: **{{ .Pipeline.Status }}**\nBuild: {{ .Repository.Slug }}",
|
||||||
template: "Status: **{{ .Pipeline.Status }}**<br>Build: {{ .Repository.Slug }}",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "render html xss template",
|
|
||||||
want: "Status: **success**\nBuild: octocat/demo",
|
|
||||||
unsafe: true,
|
|
||||||
template: "Status: **{{ .Pipeline.Status }}**<br>Build: <a href=\"javascript:alert('XSS1')\" onmouseover=\"alert('XSS2')\">{{ .Repository.Slug }}<a>",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "render markdown xss template",
|
|
||||||
want: "Status: **success**\nBuild: octocat/demo",
|
|
||||||
unsafe: true,
|
|
||||||
template: "Status: **{{ .Pipeline.Status }}**<br>Build: [{{ .Repository.Slug }}](javascript:alert(XSS1'))",
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
options := wp.Options{
|
p := New(func(_ context.Context) error { return nil })
|
||||||
Name: "wp-matrix",
|
p.Network = wp.Network{
|
||||||
Execute: func(_ context.Context) error { return nil },
|
Context: context.Background(),
|
||||||
|
Client: &http.Client{},
|
||||||
}
|
}
|
||||||
|
|
||||||
p := New(options, &Settings{})
|
|
||||||
p.Metadata = wp.Metadata{
|
p.Metadata = wp.Metadata{
|
||||||
Curr: wp.Commit{
|
Curr: wp.Commit{
|
||||||
Branch: "main",
|
Branch: "main",
|
||||||
@ -67,12 +52,12 @@ func Test_messageContent(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
p.Settings.Template = tt.template
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
p.Settings.TemplateUnsafe = tt.unsafe
|
p.Settings.Template = tt.template
|
||||||
content, _ := p.messageContent(context.Background(), http.Client{})
|
|
||||||
|
|
||||||
if content.Body != tt.want {
|
content, err := p.CreateMessage()
|
||||||
t.Errorf("messageContent: %q got: %q, want: %q", tt.name, content.Body, tt.want)
|
assert.NoError(t, err)
|
||||||
}
|
assert.Equal(t, tt.want, content)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
62
plugin/matrix.go
Normal file
62
plugin/matrix.go
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
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
|
||||||
|
}
|
106
plugin/matrix_test.go
Normal file
106
plugin/matrix_test.go
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
|
"github.com/thegeeklab/wp-matrix/plugin/mocks"
|
||||||
|
"maunium.net/go/mautrix"
|
||||||
|
"maunium.net/go/mautrix/event"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestMatrixMessageSend(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
messageOpt MatrixMessageOpt
|
||||||
|
want event.MessageEventContent
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "plain text message",
|
||||||
|
messageOpt: MatrixMessageOpt{
|
||||||
|
RoomID: "test-room",
|
||||||
|
Message: "hello world",
|
||||||
|
},
|
||||||
|
want: event.MessageEventContent{
|
||||||
|
MsgType: "m.text",
|
||||||
|
Body: "hello world",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "markdown message",
|
||||||
|
messageOpt: MatrixMessageOpt{
|
||||||
|
RoomID: "test-room",
|
||||||
|
Message: "**hello world**",
|
||||||
|
},
|
||||||
|
want: event.MessageEventContent{
|
||||||
|
MsgType: "m.text",
|
||||||
|
Body: "**hello world**",
|
||||||
|
Format: "org.matrix.custom.html",
|
||||||
|
FormattedBody: "<strong>hello world</strong>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "html message",
|
||||||
|
messageOpt: MatrixMessageOpt{
|
||||||
|
RoomID: "test-room",
|
||||||
|
Message: "hello<br>world",
|
||||||
|
TemplateUnsafe: true,
|
||||||
|
},
|
||||||
|
want: event.MessageEventContent{
|
||||||
|
MsgType: "m.text",
|
||||||
|
Body: "hello\nworld",
|
||||||
|
Format: "org.matrix.custom.html",
|
||||||
|
FormattedBody: "hello<br>world",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "safe html message",
|
||||||
|
messageOpt: MatrixMessageOpt{
|
||||||
|
RoomID: "test-room",
|
||||||
|
Message: "hello world<script>alert('XSS')</script>",
|
||||||
|
TemplateUnsafe: false,
|
||||||
|
},
|
||||||
|
want: event.MessageEventContent{
|
||||||
|
MsgType: "m.text",
|
||||||
|
Body: "hello world<script>alert('XSS')</script>",
|
||||||
|
Format: "org.matrix.custom.html",
|
||||||
|
FormattedBody: "hello world<script>alert('XSS')</script>",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unsafe html message",
|
||||||
|
messageOpt: MatrixMessageOpt{
|
||||||
|
RoomID: "test-room",
|
||||||
|
Message: "hello world<script>alert('XSS')</script>",
|
||||||
|
TemplateUnsafe: true,
|
||||||
|
},
|
||||||
|
want: event.MessageEventContent{
|
||||||
|
MsgType: "m.text",
|
||||||
|
Body: "hello world",
|
||||||
|
Format: "org.matrix.custom.html",
|
||||||
|
FormattedBody: "hello world",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
mockClient := mocks.NewMockMautrixClient(t)
|
||||||
|
m := &MatrixMessage{
|
||||||
|
Opt: tt.messageOpt,
|
||||||
|
client: mockClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
mockClient.
|
||||||
|
On("SendMessageEvent", mock.Anything, tt.messageOpt.RoomID, event.EventMessage, tt.want).
|
||||||
|
Return(&mautrix.RespSendEvent{}, nil)
|
||||||
|
|
||||||
|
err := m.Send(ctx)
|
||||||
|
assert.Equal(t, tt.wantErr, err != nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
117
plugin/mocks/mock_MautrixClient.go
Normal file
117
plugin/mocks/mock_MautrixClient.go
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
// Code generated by mockery v2.42.1. DO NOT EDIT.
|
||||||
|
|
||||||
|
package mocks
|
||||||
|
|
||||||
|
import (
|
||||||
|
context "context"
|
||||||
|
|
||||||
|
event "maunium.net/go/mautrix/event"
|
||||||
|
id "maunium.net/go/mautrix/id"
|
||||||
|
|
||||||
|
mautrix "maunium.net/go/mautrix"
|
||||||
|
|
||||||
|
mock "github.com/stretchr/testify/mock"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MockMautrixClient is an autogenerated mock type for the MautrixClient type
|
||||||
|
type MockMautrixClient struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
type MockMautrixClient_Expecter struct {
|
||||||
|
mock *mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_m *MockMautrixClient) EXPECT() *MockMautrixClient_Expecter {
|
||||||
|
return &MockMautrixClient_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) {
|
||||||
|
_va := make([]interface{}, len(extra))
|
||||||
|
for _i := range extra {
|
||||||
|
_va[_i] = extra[_i]
|
||||||
|
}
|
||||||
|
var _ca []interface{}
|
||||||
|
_ca = append(_ca, ctx, roomID, eventType, contentJSON)
|
||||||
|
_ca = append(_ca, _va...)
|
||||||
|
ret := _m.Called(_ca...)
|
||||||
|
|
||||||
|
if len(ret) == 0 {
|
||||||
|
panic("no return value specified for SendMessageEvent")
|
||||||
|
}
|
||||||
|
|
||||||
|
var r0 *mautrix.RespSendEvent
|
||||||
|
var r1 error
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, id.RoomID, event.Type, interface{}, ...mautrix.ReqSendEvent) (*mautrix.RespSendEvent, error)); ok {
|
||||||
|
return rf(ctx, roomID, eventType, contentJSON, extra...)
|
||||||
|
}
|
||||||
|
if rf, ok := ret.Get(0).(func(context.Context, id.RoomID, event.Type, interface{}, ...mautrix.ReqSendEvent) *mautrix.RespSendEvent); ok {
|
||||||
|
r0 = rf(ctx, roomID, eventType, contentJSON, extra...)
|
||||||
|
} else {
|
||||||
|
if ret.Get(0) != nil {
|
||||||
|
r0 = ret.Get(0).(*mautrix.RespSendEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if rf, ok := ret.Get(1).(func(context.Context, id.RoomID, event.Type, interface{}, ...mautrix.ReqSendEvent) error); ok {
|
||||||
|
r1 = rf(ctx, roomID, eventType, contentJSON, extra...)
|
||||||
|
} else {
|
||||||
|
r1 = ret.Error(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
*mock.Call
|
||||||
|
}
|
||||||
|
|
||||||
|
// SendMessageEvent is a helper method to define mock.On call
|
||||||
|
// - ctx context.Context
|
||||||
|
// - roomID id.RoomID
|
||||||
|
// - 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",
|
||||||
|
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 {
|
||||||
|
_c.Call.Run(func(args mock.Arguments) {
|
||||||
|
variadicArgs := make([]mautrix.ReqSendEvent, len(args)-4)
|
||||||
|
for i, a := range args[4:] {
|
||||||
|
if a != nil {
|
||||||
|
variadicArgs[i] = a.(mautrix.ReqSendEvent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
run(args[0].(context.Context), args[1].(id.RoomID), args[2].(event.Type), args[3].(interface{}), variadicArgs...)
|
||||||
|
})
|
||||||
|
return _c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (_c *MockMautrixClient_SendMessageEvent_Call) Return(resp *mautrix.RespSendEvent, err error) *MockMautrixClient_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 {
|
||||||
|
_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.
|
||||||
|
// The first argument is typically a *testing.T value.
|
||||||
|
func NewMockMautrixClient(t interface {
|
||||||
|
mock.TestingT
|
||||||
|
Cleanup(func())
|
||||||
|
}) *MockMautrixClient {
|
||||||
|
mock := &MockMautrixClient{}
|
||||||
|
mock.Mock.Test(t)
|
||||||
|
|
||||||
|
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||||
|
|
||||||
|
return mock
|
||||||
|
}
|
@ -7,9 +7,15 @@
|
|||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
wp "github.com/thegeeklab/wp-plugin-go/plugin"
|
"fmt"
|
||||||
|
|
||||||
|
wp "github.com/thegeeklab/wp-plugin-go/v2/plugin"
|
||||||
|
"github.com/urfave/cli/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:generate mockery
|
||||||
|
//go:generate go run ../internal/doc/main.go -output=../docs/data/data-raw.yaml
|
||||||
|
|
||||||
//nolint:lll
|
//nolint:lll
|
||||||
const DefaultMessageTemplate = `
|
const DefaultMessageTemplate = `
|
||||||
Status: **{{ .Pipeline.Status }}**
|
Status: **{{ .Pipeline.Status }}**
|
||||||
@ -35,13 +41,96 @@ type Settings struct {
|
|||||||
TemplateUnsafe bool
|
TemplateUnsafe bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(options wp.Options, settings *Settings) *Plugin {
|
func New(e wp.ExecuteFunc, build ...string) *Plugin {
|
||||||
p := &Plugin{}
|
p := &Plugin{
|
||||||
|
Settings: &Settings{},
|
||||||
|
}
|
||||||
|
|
||||||
options.Execute = p.run
|
options := wp.Options{
|
||||||
|
Name: "wp-matrix",
|
||||||
|
Description: "Send messages to a Matrix room",
|
||||||
|
Flags: Flags(p.Settings, wp.FlagsPluginCategory),
|
||||||
|
Execute: p.run,
|
||||||
|
HideWoodpeckerFlags: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(build) > 0 {
|
||||||
|
options.Version = build[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(build) > 1 {
|
||||||
|
options.VersionMetadata = fmt.Sprintf("date=%s", build[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
if e != nil {
|
||||||
|
options.Execute = e
|
||||||
|
}
|
||||||
|
|
||||||
p.Plugin = wp.New(options)
|
p.Plugin = wp.New(options)
|
||||||
p.Settings = settings
|
|
||||||
|
|
||||||
return p
|
return p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flags returns a slice of CLI flags for the plugin.
|
||||||
|
func Flags(settings *Settings, category string) []cli.Flag {
|
||||||
|
return []cli.Flag{
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "username",
|
||||||
|
EnvVars: []string{"PLUGIN_USERNAME", "MATRIX_USERNAME"},
|
||||||
|
Usage: "authentication username",
|
||||||
|
Destination: &settings.Username,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "password",
|
||||||
|
EnvVars: []string{"PLUGIN_PASSWORD", "MATRIX_PASSWORD"},
|
||||||
|
Usage: "authentication password",
|
||||||
|
Destination: &settings.Password,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "userid",
|
||||||
|
EnvVars: []string{"PLUGIN_USER_ID", "PLUGIN_USERID", "MATRIX_USER_ID", "MATRIX_USERID"},
|
||||||
|
Usage: "authentication user ID",
|
||||||
|
Destination: &settings.UserID,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "accesstoken",
|
||||||
|
EnvVars: []string{"PLUGIN_ACCESS_TOKEN", "PLUGIN_ACCESSTOKEN", "MATRIX_ACCESS_TOKEN", "MATRIX_ACCESSTOKEN"},
|
||||||
|
Usage: "authentication access token",
|
||||||
|
Destination: &settings.AccessToken,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "homeserver",
|
||||||
|
EnvVars: []string{"PLUGIN_HOMESERVER", "MATRIX_HOMESERVER"},
|
||||||
|
Usage: "matrix home server url",
|
||||||
|
Value: "https://matrix.org",
|
||||||
|
Destination: &settings.Homeserver,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "roomid",
|
||||||
|
EnvVars: []string{"PLUGIN_ROOMID", "MATRIX_ROOMID"},
|
||||||
|
Usage: "roomid to send messages to",
|
||||||
|
Destination: &settings.RoomID,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
&cli.StringFlag{
|
||||||
|
Name: "template",
|
||||||
|
EnvVars: []string{"PLUGIN_TEMPLATE", "MATRIX_TEMPLATE"},
|
||||||
|
Usage: "golang template for the message",
|
||||||
|
Value: DefaultMessageTemplate,
|
||||||
|
Destination: &settings.Template,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "template-unsafe",
|
||||||
|
EnvVars: []string{"PLUGIN_TEMPLATE_UNSAFE", "MATRIX_TEMPLATE_UNSAFE"},
|
||||||
|
Usage: "render raw HTML and potentially dangerous links in template",
|
||||||
|
Destination: &settings.TemplateUnsafe,
|
||||||
|
Category: category,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
16
plugin/util.go
Normal file
16
plugin/util.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// EnsurePrefix ensures that the given input string starts with the provided prefix.
|
||||||
|
func EnsurePrefix(prefix, input string) string {
|
||||||
|
if strings.TrimSpace(input) == "" {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasPrefix(input, prefix) {
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
return prefix + input
|
||||||
|
}
|
46
plugin/util_test.go
Normal file
46
plugin/util_test.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package plugin
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestEnsurePrefix(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
prefix string
|
||||||
|
input string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty input",
|
||||||
|
prefix: "pre_",
|
||||||
|
input: "",
|
||||||
|
want: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "input already has prefix",
|
||||||
|
prefix: "pre_",
|
||||||
|
input: "pre_value",
|
||||||
|
want: "pre_value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "input needs prefix",
|
||||||
|
prefix: "pre_",
|
||||||
|
input: "value",
|
||||||
|
want: "pre_value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "input with leading/trailing spaces",
|
||||||
|
prefix: "pre_",
|
||||||
|
input: " value ",
|
||||||
|
want: "pre_ value ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := EnsurePrefix(tt.prefix, tt.input)
|
||||||
|
if got != tt.want {
|
||||||
|
t.Errorf("EnsurePrefix(%q, %q) = %q, want %q", tt.prefix, tt.input, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user