summaryrefslogtreecommitdiffstats
path: root/bridge/matrix/appservice.go
diff options
context:
space:
mode:
Diffstat (limited to 'bridge/matrix/appservice.go')
-rw-r--r--bridge/matrix/appservice.go147
1 files changed, 147 insertions, 0 deletions
diff --git a/bridge/matrix/appservice.go b/bridge/matrix/appservice.go
new file mode 100644
index 00000000..ef172bff
--- /dev/null
+++ b/bridge/matrix/appservice.go
@@ -0,0 +1,147 @@
+package bmatrix
+
+import (
+ "fmt"
+ "regexp"
+
+ "github.com/sirupsen/logrus"
+
+ "maunium.net/go/mautrix/appservice"
+ "maunium.net/go/mautrix/event"
+ "maunium.net/go/mautrix/id"
+)
+
+type AppServiceNamespaces struct {
+ rooms []*regexp.Regexp
+ usernames []*regexp.Regexp
+ prefixes []string
+}
+
+type AppServiceWrapper struct {
+ appService *appservice.AppService
+ namespaces AppServiceNamespaces
+ stop chan struct{}
+ stopAck chan struct{}
+}
+
+func (w *AppServiceWrapper) ParseNamespaces(logger *logrus.Entry) error {
+ if w.appService.Registration != nil {
+ // TODO: handle non-exclusive registrations
+ for _, v := range w.appService.Registration.Namespaces.RoomIDs {
+ re, err := regexp.Compile(v.Regex)
+ if err != nil {
+ logger.Warnf("couldn't parse the appservice regex '%s'", v.Regex)
+ continue
+ }
+
+ w.namespaces.rooms = append(w.namespaces.rooms, re)
+ }
+
+ for _, v := range w.appService.Registration.Namespaces.UserIDs {
+ re, err := regexp.Compile(v.Regex)
+ if err != nil {
+ logger.Warnf("couldn't parse the appservice regex '%s'", v.Regex)
+ continue
+ }
+
+ // we assume that the user regexes will be of the form '@<some prefix>.*'
+ // where '.*' will be replaced by the username we spoof
+ prefix, _ := re.LiteralPrefix()
+ if prefix == "" || prefix == "@" {
+ logger.Warnf("couldn't find an acceptable prefix in the appservice regex '%s'", v.Regex)
+ continue
+ }
+
+ if v.Regex != fmt.Sprintf("%s.*", prefix) {
+ logger.Warnf("complex regexpes are not supported for appServices, the regexp '%s' does not match the format '@<prefix>.*'", v.Regex)
+ continue
+ }
+
+ w.namespaces.usernames = append(w.namespaces.usernames, re)
+ // drop the '@' in the prefix
+ w.namespaces.prefixes = append(w.namespaces.prefixes, prefix[1:])
+ }
+ }
+
+ return nil
+}
+
+func (b *Bmatrix) NewAppService() (*AppServiceWrapper, error) {
+ w := &AppServiceWrapper{
+ appService: appservice.Create(),
+ namespaces: AppServiceNamespaces{
+ rooms: []*regexp.Regexp{},
+ usernames: []*regexp.Regexp{},
+ prefixes: []string{},
+ },
+ stop: make(chan struct{}, 1),
+ stopAck: make(chan struct{}, 1),
+ }
+
+ err := w.appService.SetHomeserverURL(b.mc.HomeserverURL.String())
+ if err != nil {
+ return nil, err
+ }
+
+ _, homeServerDomain, _ := b.mc.UserID.Parse()
+ w.appService.HomeserverDomain = homeServerDomain
+ //nolint:exhaustruct
+ w.appService.Host = appservice.HostConfig{
+ Hostname: b.GetString("AppServiceHost"),
+ Port: uint16(b.GetInt("AppServicePort")),
+ }
+ w.appService.Registration, err = appservice.LoadRegistration(b.GetString("AppServiceConfigPath"))
+ if err != nil {
+ return nil, err
+ }
+
+ // forward logs from the appService to the matterbridge logger
+ w.appService.Log = NewZerologWrapper(b.Log)
+
+ if err = w.ParseNamespaces(b.Log); err != nil {
+ return nil, err
+ }
+
+ return w, nil
+}
+
+func (a *AppServiceNamespaces) containsRoom(roomID id.RoomID) bool {
+ // no room specified: we check all the rooms
+ if len(a.rooms) == 0 {
+ return true
+ }
+
+ for _, room := range a.rooms {
+ if room.MatchString(roomID.String()) {
+ return true
+ }
+ }
+
+ return false
+}
+
+// nolint: wrapcheck
+func (b *Bmatrix) startAppService() error {
+ wrapper := b.appService
+ // TODO: detect service completion and rerun automatically
+ go wrapper.appService.Start()
+ b.Log.Debug("appservice launched")
+
+ processor := appservice.NewEventProcessor(wrapper.appService)
+ processor.On(event.EventMessage, func(ev *event.Event) {
+ b.handleEvent(originAppService, ev)
+ })
+ go processor.Start()
+ b.Log.Debug("appservice event dispatcher launched")
+
+ // handle service stopping/restarting
+ go func(appService *appservice.AppService, processor *appservice.EventProcessor) {
+ <-wrapper.stop
+
+ appService.Stop()
+ processor.Stop()
+ wrapper.stopAck <- struct{}{}
+ }(wrapper.appService, processor)
+
+ return nil
+}