1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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
}
|