mirror of https://github.com/mautrix/go.git
parent
61b0553ae9
commit
c4ce54d9d6
|
@ -7,13 +7,17 @@
|
|||
package pushrules
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/tidwall/gjson"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/pushrules/glob"
|
||||
)
|
||||
|
||||
|
@ -23,6 +27,12 @@ type Room interface {
|
|||
GetMemberCount() int
|
||||
}
|
||||
|
||||
// EventfulRoom is an extension of Room to support MSC3664.
|
||||
type EventfulRoom interface {
|
||||
Room
|
||||
GetEvent(id.EventID) *event.Event
|
||||
}
|
||||
|
||||
// PushCondKind is the type of a push condition.
|
||||
type PushCondKind string
|
||||
|
||||
|
@ -31,6 +41,11 @@ const (
|
|||
KindEventMatch PushCondKind = "event_match"
|
||||
KindContainsDisplayName PushCondKind = "contains_display_name"
|
||||
KindRoomMemberCount PushCondKind = "room_member_count"
|
||||
|
||||
// MSC3664: https://github.com/matrix-org/matrix-spec-proposals/pull/3664
|
||||
|
||||
KindRelatedEventMatch PushCondKind = "related_event_match"
|
||||
KindUnstableRelatedEventMatch PushCondKind = "im.nheko.msc3664.related_event_match"
|
||||
)
|
||||
|
||||
// PushCondition wraps a condition that is required for a specific PushRule to be used.
|
||||
|
@ -44,6 +59,9 @@ type PushCondition struct {
|
|||
// The condition that needs to be fulfilled for RoomMemberCount-type conditions.
|
||||
// A decimal integer optionally prefixed by ==, <, >, >= or <=. Prefix "==" is assumed if no prefix found.
|
||||
MemberCountCondition string `json:"is,omitempty"`
|
||||
|
||||
// The relation type for related_event_match from MSC3664
|
||||
RelType event.RelationType `json:"rel_type,omitempty"`
|
||||
}
|
||||
|
||||
// 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 {
|
|||
switch cond.Kind {
|
||||
case KindEventMatch:
|
||||
return cond.matchValue(room, evt)
|
||||
case KindRelatedEventMatch, KindUnstableRelatedEventMatch:
|
||||
return cond.matchRelatedEvent(room, evt)
|
||||
case KindContainsDisplayName:
|
||||
return cond.matchDisplayName(room, evt)
|
||||
case KindRoomMemberCount:
|
||||
|
@ -148,6 +168,47 @@ func (cond *PushCondition) matchValue(room Room, evt *event.Event) bool {
|
|||
}
|
||||
}
|
||||
|
||||
func (cond *PushCondition) getRelationEventID(relatesTo *event.RelatesTo) id.EventID {
|
||||
if relatesTo == nil {
|
||||
return ""
|
||||
}
|
||||
switch cond.RelType {
|
||||
case "":
|
||||
return relatesTo.EventID
|
||||
case "m.in_reply_to":
|
||||
if relatesTo.IsFallingBack || relatesTo.InReplyTo == nil {
|
||||
return ""
|
||||
}
|
||||
return relatesTo.InReplyTo.EventID
|
||||
default:
|
||||
if relatesTo.Type != cond.RelType {
|
||||
return ""
|
||||
}
|
||||
return relatesTo.EventID
|
||||
}
|
||||
}
|
||||
|
||||
func (cond *PushCondition) matchRelatedEvent(room Room, evt *event.Event) bool {
|
||||
var relatesTo *event.RelatesTo
|
||||
if relatable, ok := evt.Content.Parsed.(event.Relatable); ok {
|
||||
relatesTo = relatable.OptionalGetRelatesTo()
|
||||
} else {
|
||||
res := gjson.GetBytes(evt.Content.VeryRaw, `m\.relates_to`)
|
||||
if res.Exists() && res.IsObject() {
|
||||
_ = json.Unmarshal([]byte(res.Str), &relatesTo)
|
||||
}
|
||||
}
|
||||
if evtID := cond.getRelationEventID(relatesTo); evtID == "" {
|
||||
return false
|
||||
} else if eventfulRoom, ok := room.(EventfulRoom); !ok {
|
||||
return false
|
||||
} else if evt = eventfulRoom.GetEvent(relatesTo.EventID); evt == nil {
|
||||
return false
|
||||
} else {
|
||||
return cond.matchValue(room, evt)
|
||||
}
|
||||
}
|
||||
|
||||
func (cond *PushCondition) matchDisplayName(room Room, evt *event.Event) bool {
|
||||
displayname := room.GetOwnDisplayname()
|
||||
if len(displayname) == 0 {
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/id"
|
||||
"maunium.net/go/mautrix/pushrules"
|
||||
)
|
||||
|
||||
|
@ -104,12 +105,15 @@ func TestPushCondition_Match_InvalidKind(t *testing.T) {
|
|||
type FakeRoom struct {
|
||||
members map[string]*event.MemberEventContent
|
||||
owner string
|
||||
|
||||
events map[id.EventID]*event.Event
|
||||
}
|
||||
|
||||
func newFakeRoom(memberCount int) *FakeRoom {
|
||||
room := &FakeRoom{
|
||||
owner: "@tulir:maunium.net",
|
||||
members: make(map[string]*event.MemberEventContent),
|
||||
events: make(map[id.EventID]*event.Event),
|
||||
}
|
||||
|
||||
if memberCount >= 1 {
|
||||
|
@ -141,3 +145,7 @@ func (fr *FakeRoom) GetOwnDisplayname() string {
|
|||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (fr *FakeRoom) GetEvent(evtID id.EventID) *event.Event {
|
||||
return fr.events[evtID]
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ func TestPushRule_Match_Conditions_NestedKey_Boolean(t *testing.T) {
|
|||
Conditions: []*pushrules.PushCondition{cond1},
|
||||
}
|
||||
|
||||
evt := newFakeEvent(event.EventMessage, &event.MemberEventContent{
|
||||
evt := newFakeEvent(event.StateMember, &event.MemberEventContent{
|
||||
Membership: "invite",
|
||||
})
|
||||
assert.False(t, rule.Match(blankTestRoom, evt))
|
||||
|
@ -86,7 +86,7 @@ func TestPushRule_Match_Conditions_EscapedKey(t *testing.T) {
|
|||
Conditions: []*pushrules.PushCondition{cond1},
|
||||
}
|
||||
|
||||
evt := newFakeEvent(event.EventMessage, &event.MemberEventContent{
|
||||
evt := newFakeEvent(event.StateMember, &event.MemberEventContent{
|
||||
Membership: "invite",
|
||||
})
|
||||
assert.False(t, rule.Match(blankTestRoom, evt))
|
||||
|
@ -102,7 +102,7 @@ func TestPushRule_Match_Conditions_EscapedKey_NoNesting(t *testing.T) {
|
|||
Conditions: []*pushrules.PushCondition{cond1},
|
||||
}
|
||||
|
||||
evt := newFakeEvent(event.EventMessage, &event.MemberEventContent{
|
||||
evt := newFakeEvent(event.StateMember, &event.MemberEventContent{
|
||||
Membership: "invite",
|
||||
})
|
||||
assert.False(t, rule.Match(blankTestRoom, evt))
|
||||
|
@ -112,6 +112,34 @@ func TestPushRule_Match_Conditions_EscapedKey_NoNesting(t *testing.T) {
|
|||
assert.False(t, rule.Match(blankTestRoom, evt))
|
||||
}
|
||||
|
||||
func TestPushRule_Match_Conditions_RelatedEvent(t *testing.T) {
|
||||
cond1 := &pushrules.PushCondition{
|
||||
Kind: pushrules.KindRelatedEventMatch,
|
||||
Key: "sender",
|
||||
Pattern: "@tulir:maunium.net",
|
||||
}
|
||||
rule := &pushrules.PushRule{
|
||||
Type: pushrules.OverrideRule,
|
||||
Enabled: true,
|
||||
Conditions: []*pushrules.PushCondition{cond1},
|
||||
}
|
||||
|
||||
evt := newFakeEvent(event.EventReaction, &event.ReactionEventContent{
|
||||
RelatesTo: event.RelatesTo{
|
||||
Type: event.RelAnnotation,
|
||||
EventID: "$meow",
|
||||
Key: "🐈️",
|
||||
},
|
||||
})
|
||||
roomWithEvent := newFakeRoom(1)
|
||||
assert.False(t, rule.Match(roomWithEvent, evt))
|
||||
roomWithEvent.events["$meow"] = newFakeEvent(event.EventMessage, &event.MessageEventContent{
|
||||
MsgType: event.MsgEmote,
|
||||
Body: "is testing pushrules",
|
||||
})
|
||||
assert.True(t, rule.Match(roomWithEvent, evt))
|
||||
}
|
||||
|
||||
func TestPushRule_Match_Conditions_Disabled(t *testing.T) {
|
||||
cond1 := newMatchPushCondition("content.msgtype", "m.emote")
|
||||
cond2 := newMatchPushCondition("content.body", "*pushrules")
|
||||
|
|
Loading…
Reference in New Issue