7
7
package pushrules
8
8
9
9
import (
10
+ "encoding/json"
10
11
"fmt"
11
12
"regexp"
12
13
"strconv"
13
14
"strings"
14
15
"unicode"
15
16
17
+ "github.com/tidwall/gjson"
18
+
16
19
"maunium.net/go/mautrix/event"
20
+ "maunium.net/go/mautrix/id"
17
21
"maunium.net/go/mautrix/pushrules/glob"
18
22
)
19
23
@@ -23,6 +27,12 @@ type Room interface {
23
27
GetMemberCount () int
24
28
}
25
29
30
+ // EventfulRoom is an extension of Room to support MSC3664.
31
+ type EventfulRoom interface {
32
+ Room
33
+ GetEvent (id.EventID ) * event.Event
34
+ }
35
+
26
36
// PushCondKind is the type of a push condition.
27
37
type PushCondKind string
28
38
@@ -31,6 +41,11 @@ const (
31
41
KindEventMatch PushCondKind = "event_match"
32
42
KindContainsDisplayName PushCondKind = "contains_display_name"
33
43
KindRoomMemberCount PushCondKind = "room_member_count"
44
+
45
+ // MSC3664: https://github.com/matrix-org/matrix-spec-proposals/pull/3664
46
+
47
+ KindRelatedEventMatch PushCondKind = "related_event_match"
48
+ KindUnstableRelatedEventMatch PushCondKind = "im.nheko.msc3664.related_event_match"
34
49
)
35
50
36
51
// PushCondition wraps a condition that is required for a specific PushRule to be used.
@@ -44,6 +59,9 @@ type PushCondition struct {
44
59
// The condition that needs to be fulfilled for RoomMemberCount-type conditions.
45
60
// A decimal integer optionally prefixed by ==, <, >, >= or <=. Prefix "==" is assumed if no prefix found.
46
61
MemberCountCondition string `json:"is,omitempty"`
62
+
63
+ // The relation type for related_event_match from MSC3664
64
+ RelType event.RelationType `json:"rel_type,omitempty"`
47
65
}
48
66
49
67
// MemberCountFilterRegex is the regular expression to parse the MemberCountCondition of PushConditions.
@@ -54,6 +72,8 @@ func (cond *PushCondition) Match(room Room, evt *event.Event) bool {
54
72
switch cond .Kind {
55
73
case KindEventMatch :
56
74
return cond .matchValue (room , evt )
75
+ case KindRelatedEventMatch , KindUnstableRelatedEventMatch :
76
+ return cond .matchRelatedEvent (room , evt )
57
77
case KindContainsDisplayName :
58
78
return cond .matchDisplayName (room , evt )
59
79
case KindRoomMemberCount :
@@ -148,6 +168,47 @@ func (cond *PushCondition) matchValue(room Room, evt *event.Event) bool {
148
168
}
149
169
}
150
170
171
+ func (cond * PushCondition ) getRelationEventID (relatesTo * event.RelatesTo ) id.EventID {
172
+ if relatesTo == nil {
173
+ return ""
174
+ }
175
+ switch cond .RelType {
176
+ case "" :
177
+ return relatesTo .EventID
178
+ case "m.in_reply_to" :
179
+ if relatesTo .IsFallingBack || relatesTo .InReplyTo == nil {
180
+ return ""
181
+ }
182
+ return relatesTo .InReplyTo .EventID
183
+ default :
184
+ if relatesTo .Type != cond .RelType {
185
+ return ""
186
+ }
187
+ return relatesTo .EventID
188
+ }
189
+ }
190
+
191
+ func (cond * PushCondition ) matchRelatedEvent (room Room , evt * event.Event ) bool {
192
+ var relatesTo * event.RelatesTo
193
+ if relatable , ok := evt .Content .Parsed .(event.Relatable ); ok {
194
+ relatesTo = relatable .OptionalGetRelatesTo ()
195
+ } else {
196
+ res := gjson .GetBytes (evt .Content .VeryRaw , `m\.relates_to` )
197
+ if res .Exists () && res .IsObject () {
198
+ _ = json .Unmarshal ([]byte (res .Str ), & relatesTo )
199
+ }
200
+ }
201
+ if evtID := cond .getRelationEventID (relatesTo ); evtID == "" {
202
+ return false
203
+ } else if eventfulRoom , ok := room .(EventfulRoom ); ! ok {
204
+ return false
205
+ } else if evt = eventfulRoom .GetEvent (relatesTo .EventID ); evt == nil {
206
+ return false
207
+ } else {
208
+ return cond .matchValue (room , evt )
209
+ }
210
+ }
211
+
151
212
func (cond * PushCondition ) matchDisplayName (room Room , evt * event.Event ) bool {
152
213
displayname := room .GetOwnDisplayname ()
153
214
if len (displayname ) == 0 {
0 commit comments