diff options
Diffstat (limited to 'matrix/pushrules')
-rw-r--r-- | matrix/pushrules/action.go | 134 | ||||
-rw-r--r-- | matrix/pushrules/action_test.go | 210 | ||||
-rw-r--r-- | matrix/pushrules/condition.go | 162 | ||||
-rw-r--r-- | matrix/pushrules/condition_displayname_test.go | 60 | ||||
-rw-r--r-- | matrix/pushrules/condition_eventmatch_test.go | 96 | ||||
-rw-r--r-- | matrix/pushrules/condition_membercount_test.go | 71 | ||||
-rw-r--r-- | matrix/pushrules/condition_test.go | 132 | ||||
-rw-r--r-- | matrix/pushrules/doc.go | 2 | ||||
-rw-r--r-- | matrix/pushrules/pushrules.go | 37 | ||||
-rw-r--r-- | matrix/pushrules/pushrules_test.go | 249 | ||||
-rw-r--r-- | matrix/pushrules/rule.go | 160 | ||||
-rw-r--r-- | matrix/pushrules/rule_array_test.go | 294 | ||||
-rw-r--r-- | matrix/pushrules/rule_test.go | 195 | ||||
-rw-r--r-- | matrix/pushrules/ruleset.go | 98 |
14 files changed, 0 insertions, 1900 deletions
diff --git a/matrix/pushrules/action.go b/matrix/pushrules/action.go deleted file mode 100644 index 4637950..0000000 --- a/matrix/pushrules/action.go +++ /dev/null @@ -1,134 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules - -import "encoding/json" - -// PushActionType is the type of a PushAction -type PushActionType string - -// The allowed push action types as specified in spec section 11.12.1.4.1. -const ( - ActionNotify PushActionType = "notify" - ActionDontNotify PushActionType = "dont_notify" - ActionCoalesce PushActionType = "coalesce" - ActionSetTweak PushActionType = "set_tweak" -) - -// PushActionTweak is the type of the tweak in SetTweak push actions. -type PushActionTweak string - -// The allowed tweak types as specified in spec section 11.12.1.4.1.1. -const ( - TweakSound PushActionTweak = "sound" - TweakHighlight PushActionTweak = "highlight" -) - -// PushActionArray is an array of PushActions. -type PushActionArray []*PushAction - -// PushActionArrayShould contains the important information parsed from a PushActionArray. -type PushActionArrayShould struct { - // Whether or not the array contained a Notify, DontNotify or Coalesce action type. - NotifySpecified bool - // Whether or not the event in question should trigger a notification. - Notify bool - // Whether or not the event in question should be highlighted. - Highlight bool - - // Whether or not the event in question should trigger a sound alert. - PlaySound bool - // The name of the sound to play if PlaySound is true. - SoundName string -} - -// Should parses this push action array and returns the relevant details wrapped in a PushActionArrayShould struct. -func (actions PushActionArray) Should() (should PushActionArrayShould) { - for _, action := range actions { - switch action.Action { - case ActionNotify, ActionCoalesce: - should.Notify = true - should.NotifySpecified = true - case ActionDontNotify: - should.Notify = false - should.NotifySpecified = true - case ActionSetTweak: - switch action.Tweak { - case TweakHighlight: - var ok bool - should.Highlight, ok = action.Value.(bool) - if !ok { - // Highlight value not specified, so assume true since the tweak is set. - should.Highlight = true - } - case TweakSound: - should.SoundName = action.Value.(string) - should.PlaySound = len(should.SoundName) > 0 - } - } - } - return -} - -// PushAction is a single action that should be triggered when receiving a message. -type PushAction struct { - Action PushActionType - Tweak PushActionTweak - Value interface{} -} - -// UnmarshalJSON parses JSON into this PushAction. -// -// * If the JSON is a single string, the value is stored in the Action field. -// * If the JSON is an object with the set_tweak field, Action will be set to -// "set_tweak", Tweak will be set to the value of the set_tweak field and -// and Value will be set to the value of the value field. -// * In any other case, the function does nothing. -func (action *PushAction) UnmarshalJSON(raw []byte) error { - var data interface{} - - err := json.Unmarshal(raw, &data) - if err != nil { - return err - } - - switch val := data.(type) { - case string: - action.Action = PushActionType(val) - case map[string]interface{}: - tweak, ok := val["set_tweak"].(string) - if ok { - action.Action = ActionSetTweak - action.Tweak = PushActionTweak(tweak) - action.Value, _ = val["value"] - } - } - return nil -} - -// MarshalJSON is the reverse of UnmarshalJSON() -func (action *PushAction) MarshalJSON() (raw []byte, err error) { - if action.Action == ActionSetTweak { - data := map[string]interface{}{ - "set_tweak": action.Tweak, - "value": action.Value, - } - return json.Marshal(&data) - } - data := string(action.Action) - return json.Marshal(&data) -} diff --git a/matrix/pushrules/action_test.go b/matrix/pushrules/action_test.go deleted file mode 100644 index 79b2fdf..0000000 --- a/matrix/pushrules/action_test.go +++ /dev/null @@ -1,210 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "maunium.net/go/gomuks/matrix/pushrules" -) - -func TestPushActionArray_Should_EmptyArrayReturnsDefaults(t *testing.T) { - should := pushrules.PushActionArray{}.Should() - assert.False(t, should.NotifySpecified) - assert.False(t, should.Notify) - assert.False(t, should.Highlight) - assert.False(t, should.PlaySound) - assert.Empty(t, should.SoundName) -} - -func TestPushActionArray_Should_MixedArrayReturnsExpected1(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "ping"}, - }.Should() - assert.True(t, should.NotifySpecified) - assert.True(t, should.Notify) - assert.True(t, should.Highlight) - assert.True(t, should.PlaySound) - assert.Equal(t, "ping", should.SoundName) -} - -func TestPushActionArray_Should_MixedArrayReturnsExpected2(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: ""}, - }.Should() - assert.True(t, should.NotifySpecified) - assert.False(t, should.Notify) - assert.False(t, should.Highlight) - assert.False(t, should.PlaySound) - assert.Empty(t, should.SoundName) -} - -func TestPushActionArray_Should_NotifySet(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - }.Should() - assert.True(t, should.NotifySpecified) - assert.True(t, should.Notify) - assert.False(t, should.Highlight) - assert.False(t, should.PlaySound) - assert.Empty(t, should.SoundName) -} - -func TestPushActionArray_Should_NotifyAndCoalesceDoTheSameThing(t *testing.T) { - should1 := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - }.Should() - should2 := pushrules.PushActionArray{ - {Action: pushrules.ActionCoalesce}, - }.Should() - assert.Equal(t, should1, should2) -} - -func TestPushActionArray_Should_DontNotify(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - }.Should() - assert.True(t, should.NotifySpecified) - assert.False(t, should.Notify) - assert.False(t, should.Highlight) - assert.False(t, should.PlaySound) - assert.Empty(t, should.SoundName) -} - -func TestPushActionArray_Should_HighlightBlank(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight}, - }.Should() - assert.False(t, should.NotifySpecified) - assert.False(t, should.Notify) - assert.True(t, should.Highlight) - assert.False(t, should.PlaySound) - assert.Empty(t, should.SoundName) -} - -func TestPushActionArray_Should_HighlightFalse(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - }.Should() - assert.False(t, should.NotifySpecified) - assert.False(t, should.Notify) - assert.False(t, should.Highlight) - assert.False(t, should.PlaySound) - assert.Empty(t, should.SoundName) -} - -func TestPushActionArray_Should_SoundName(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "ping"}, - }.Should() - assert.False(t, should.NotifySpecified) - assert.False(t, should.Notify) - assert.False(t, should.Highlight) - assert.True(t, should.PlaySound) - assert.Equal(t, "ping", should.SoundName) -} - -func TestPushActionArray_Should_SoundNameEmpty(t *testing.T) { - should := pushrules.PushActionArray{ - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: ""}, - }.Should() - assert.False(t, should.NotifySpecified) - assert.False(t, should.Notify) - assert.False(t, should.Highlight) - assert.False(t, should.PlaySound) - assert.Empty(t, should.SoundName) -} - -func TestPushAction_UnmarshalJSON_InvalidJSONFails(t *testing.T) { - pa := &pushrules.PushAction{} - err := pa.UnmarshalJSON([]byte("Not JSON")) - assert.NotNil(t, err) -} - -func TestPushAction_UnmarshalJSON_InvalidTypeDoesNothing(t *testing.T) { - pa := &pushrules.PushAction{ - Action: pushrules.PushActionType("unchanged"), - Tweak: pushrules.PushActionTweak("unchanged"), - Value: "unchanged", - } - - err := pa.UnmarshalJSON([]byte(`{"foo": "bar"}`)) - assert.Nil(t, err) - err = pa.UnmarshalJSON([]byte(`9001`)) - assert.Nil(t, err) - - assert.Equal(t, pushrules.PushActionType("unchanged"), pa.Action) - assert.Equal(t, pushrules.PushActionTweak("unchanged"), pa.Tweak) - assert.Equal(t, "unchanged", pa.Value) -} - -func TestPushAction_UnmarshalJSON_StringChangesActionType(t *testing.T) { - pa := &pushrules.PushAction{ - Action: pushrules.PushActionType("unchanged"), - Tweak: pushrules.PushActionTweak("unchanged"), - Value: "unchanged", - } - - err := pa.UnmarshalJSON([]byte(`"foo"`)) - assert.Nil(t, err) - - assert.Equal(t, pushrules.PushActionType("foo"), pa.Action) - assert.Equal(t, pushrules.PushActionTweak("unchanged"), pa.Tweak) - assert.Equal(t, "unchanged", pa.Value) -} - -func TestPushAction_UnmarshalJSON_SetTweakChangesTweak(t *testing.T) { - pa := &pushrules.PushAction{ - Action: pushrules.PushActionType("unchanged"), - Tweak: pushrules.PushActionTweak("unchanged"), - Value: "unchanged", - } - - err := pa.UnmarshalJSON([]byte(`{"set_tweak": "foo", "value": 123.0}`)) - assert.Nil(t, err) - - assert.Equal(t, pushrules.ActionSetTweak, pa.Action) - assert.Equal(t, pushrules.PushActionTweak("foo"), pa.Tweak) - assert.Equal(t, 123.0, pa.Value) -} - -func TestPushAction_MarshalJSON_TweakOutputWorks(t *testing.T) { - pa := &pushrules.PushAction{ - Action: pushrules.ActionSetTweak, - Tweak: pushrules.PushActionTweak("foo"), - Value: "bar", - } - data, err := pa.MarshalJSON() - assert.Nil(t, err) - assert.Equal(t, []byte(`{"set_tweak":"foo","value":"bar"}`), data) -} - -func TestPushAction_MarshalJSON_OtherOutputWorks(t *testing.T) { - pa := &pushrules.PushAction{ - Action: pushrules.PushActionType("something else"), - Tweak: pushrules.PushActionTweak("foo"), - Value: "bar", - } - data, err := pa.MarshalJSON() - assert.Nil(t, err) - assert.Equal(t, []byte(`"something else"`), data) -} diff --git a/matrix/pushrules/condition.go b/matrix/pushrules/condition.go deleted file mode 100644 index cc62da1..0000000 --- a/matrix/pushrules/condition.go +++ /dev/null @@ -1,162 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules - -import ( - "regexp" - "strconv" - "strings" - "unicode" - - "maunium.net/go/gomuks/matrix/rooms" - "maunium.net/go/mautrix" - - "maunium.net/go/gomuks/lib/glob" -) - -// Room is an interface with the functions that are needed for processing room-specific push conditions -type Room interface { - GetMember(mxid string) *rooms.Member - GetMembers() map[string]*rooms.Member - GetSessionOwner() string -} - -// PushCondKind is the type of a push condition. -type PushCondKind string - -// The allowed push condition kinds as specified in section 11.12.1.4.3 of r0.3.0 of the Client-Server API. -const ( - KindEventMatch PushCondKind = "event_match" - KindContainsDisplayName PushCondKind = "contains_display_name" - KindRoomMemberCount PushCondKind = "room_member_count" -) - -// PushCondition wraps a condition that is required for a specific PushRule to be used. -type PushCondition struct { - // The type of the condition. - Kind PushCondKind `json:"kind"` - // The dot-separated field of the event to match. Only applicable if kind is EventMatch. - Key string `json:"key,omitempty"` - // The glob-style pattern to match the field against. Only applicable if kind is EventMatch. - Pattern string `json:"pattern,omitempty"` - // 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"` -} - -// MemberCountFilterRegex is the regular expression to parse the MemberCountCondition of PushConditions. -var MemberCountFilterRegex = regexp.MustCompile("^(==|[<>]=?)?([0-9]+)$") - -// Match checks if this condition is fulfilled for the given event in the given room. -func (cond *PushCondition) Match(room Room, event *mautrix.Event) bool { - switch cond.Kind { - case KindEventMatch: - return cond.matchValue(room, event) - case KindContainsDisplayName: - return cond.matchDisplayName(room, event) - case KindRoomMemberCount: - return cond.matchMemberCount(room, event) - default: - return false - } -} - -func (cond *PushCondition) matchValue(room Room, event *mautrix.Event) bool { - index := strings.IndexRune(cond.Key, '.') - key := cond.Key - subkey := "" - if index > 0 { - subkey = key[index+1:] - key = key[0:index] - } - - pattern, err := glob.Compile(cond.Pattern) - if err != nil { - return false - } - - switch key { - case "type": - return pattern.MatchString(event.Type.String()) - case "sender": - return pattern.MatchString(event.Sender) - case "room_id": - return pattern.MatchString(event.RoomID) - case "state_key": - if event.StateKey == nil { - return cond.Pattern == "" - } - return pattern.MatchString(*event.StateKey) - case "content": - val, _ := event.Content.Raw[subkey].(string) - return pattern.MatchString(val) - default: - return false - } -} - -func (cond *PushCondition) matchDisplayName(room Room, event *mautrix.Event) bool { - ownerID := room.GetSessionOwner() - if ownerID == event.Sender { - return false - } - member := room.GetMember(ownerID) - if member == nil { - return false - } - - msg := event.Content.Body - isAcceptable := func(r uint8) bool { - return unicode.IsSpace(rune(r)) || unicode.IsPunct(rune(r)) - } - length := len(member.Displayname) - for index := strings.Index(msg, member.Displayname); index != -1; index = strings.Index(msg, member.Displayname) { - if (index <= 0 || isAcceptable(msg[index-1])) && (index + length >= len(msg) || isAcceptable(msg[index+length])) { - return true - } - msg = msg[index+len(member.Displayname):] - } - return false -} - -func (cond *PushCondition) matchMemberCount(room Room, event *mautrix.Event) bool { - group := MemberCountFilterRegex.FindStringSubmatch(cond.MemberCountCondition) - if len(group) != 3 { - return false - } - - operator := group[1] - wantedMemberCount, _ := strconv.Atoi(group[2]) - - memberCount := len(room.GetMembers()) - - switch operator { - case "==", "": - return memberCount == wantedMemberCount - case ">": - return memberCount > wantedMemberCount - case ">=": - return memberCount >= wantedMemberCount - case "<": - return memberCount < wantedMemberCount - case "<=": - return memberCount <= wantedMemberCount - default: - // Should be impossible due to regex. - return false - } -} diff --git a/matrix/pushrules/condition_displayname_test.go b/matrix/pushrules/condition_displayname_test.go deleted file mode 100644 index fd3f374..0000000 --- a/matrix/pushrules/condition_displayname_test.go +++ /dev/null @@ -1,60 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "maunium.net/go/mautrix" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPushCondition_Match_DisplayName(t *testing.T) { - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgText, - Body: "tulir: test mention", - }) - event.Sender = "@someone_else:matrix.org" - assert.True(t, displaynamePushCondition.Match(displaynameTestRoom, event)) -} - -func TestPushCondition_Match_DisplayName_Fail(t *testing.T) { - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgText, - Body: "not a mention", - }) - event.Sender = "@someone_else:matrix.org" - assert.False(t, displaynamePushCondition.Match(displaynameTestRoom, event)) -} - -func TestPushCondition_Match_DisplayName_CantHighlightSelf(t *testing.T) { - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgText, - Body: "tulir: I can't highlight myself", - }) - assert.False(t, displaynamePushCondition.Match(displaynameTestRoom, event)) -} - -func TestPushCondition_Match_DisplayName_FailsOnEmptyRoom(t *testing.T) { - emptyRoom := newFakeRoom(0) - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgText, - Body: "tulir: this room doesn't have the owner Member available, so it fails.", - }) - event.Sender = "@someone_else:matrix.org" - assert.False(t, displaynamePushCondition.Match(emptyRoom, event)) -} diff --git a/matrix/pushrules/condition_eventmatch_test.go b/matrix/pushrules/condition_eventmatch_test.go deleted file mode 100644 index e5761fc..0000000 --- a/matrix/pushrules/condition_eventmatch_test.go +++ /dev/null @@ -1,96 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "maunium.net/go/mautrix" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPushCondition_Match_KindEvent_MsgType(t *testing.T) { - condition := newMatchPushCondition("content.msgtype", "m.emote") - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - Raw: map[string]interface{}{ - "msgtype": "m.emote", - "body": "tests gomuks pushconditions", - }, - }) - assert.True(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_MsgType_Fail(t *testing.T) { - condition := newMatchPushCondition("content.msgtype", "m.emote") - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - Raw: map[string]interface{}{ - "msgtype": "m.text", - "body": "I'm testing gomuks pushconditions", - }, - }) - assert.False(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_EventType(t *testing.T) { - condition := newMatchPushCondition("type", "m.room.foo") - event := newFakeEvent(mautrix.NewEventType("m.room.foo"), mautrix.Content{}) - assert.True(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_EventType_IllegalGlob(t *testing.T) { - condition := newMatchPushCondition("type", "m.room.invalid_glo[b") - event := newFakeEvent(mautrix.NewEventType("m.room.invalid_glob"), mautrix.Content{}) - assert.False(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_Sender_Fail(t *testing.T) { - condition := newMatchPushCondition("sender", "@foo:maunium.net") - event := newFakeEvent(mautrix.NewEventType("m.room.foo"), mautrix.Content{}) - assert.False(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_RoomID(t *testing.T) { - condition := newMatchPushCondition("room_id", "!fakeroom:maunium.net") - event := newFakeEvent(mautrix.NewEventType(""), mautrix.Content{}) - assert.True(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_BlankStateKey(t *testing.T) { - condition := newMatchPushCondition("state_key", "") - event := newFakeEvent(mautrix.NewEventType("m.room.foo"), mautrix.Content{}) - assert.True(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_BlankStateKey_Fail(t *testing.T) { - condition := newMatchPushCondition("state_key", "not blank") - event := newFakeEvent(mautrix.NewEventType("m.room.foo"), mautrix.Content{}) - assert.False(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_NonBlankStateKey(t *testing.T) { - condition := newMatchPushCondition("state_key", "*:maunium.net") - event := newFakeEvent(mautrix.NewEventType("m.room.foo"), mautrix.Content{}) - event.StateKey = &event.Sender - assert.True(t, condition.Match(blankTestRoom, event)) -} - -func TestPushCondition_Match_KindEvent_UnknownKey(t *testing.T) { - condition := newMatchPushCondition("non-existent key", "doesn't affect anything") - event := newFakeEvent(mautrix.NewEventType("m.room.foo"), mautrix.Content{}) - assert.False(t, condition.Match(blankTestRoom, event)) -} diff --git a/matrix/pushrules/condition_membercount_test.go b/matrix/pushrules/condition_membercount_test.go deleted file mode 100644 index ad5da9f..0000000 --- a/matrix/pushrules/condition_membercount_test.go +++ /dev/null @@ -1,71 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestPushCondition_Match_KindMemberCount_OneToOne_ImplicitPrefix(t *testing.T) { - condition := newCountPushCondition("2") - room := newFakeRoom(2) - assert.True(t, condition.Match(room, countConditionTestEvent)) -} - -func TestPushCondition_Match_KindMemberCount_OneToOne_ExplicitPrefix(t *testing.T) { - condition := newCountPushCondition("==2") - room := newFakeRoom(2) - assert.True(t, condition.Match(room, countConditionTestEvent)) -} - -func TestPushCondition_Match_KindMemberCount_BigRoom(t *testing.T) { - condition := newCountPushCondition(">200") - room := newFakeRoom(201) - assert.True(t, condition.Match(room, countConditionTestEvent)) -} - -func TestPushCondition_Match_KindMemberCount_BigRoom_Fail(t *testing.T) { - condition := newCountPushCondition(">=200") - room := newFakeRoom(199) - assert.False(t, condition.Match(room, countConditionTestEvent)) -} - -func TestPushCondition_Match_KindMemberCount_SmallRoom(t *testing.T) { - condition := newCountPushCondition("<10") - room := newFakeRoom(9) - assert.True(t, condition.Match(room, countConditionTestEvent)) -} - -func TestPushCondition_Match_KindMemberCount_SmallRoom_Fail(t *testing.T) { - condition := newCountPushCondition("<=10") - room := newFakeRoom(11) - assert.False(t, condition.Match(room, countConditionTestEvent)) -} - -func TestPushCondition_Match_KindMemberCount_InvalidPrefix(t *testing.T) { - condition := newCountPushCondition("??10") - room := newFakeRoom(11) - assert.False(t, condition.Match(room, countConditionTestEvent)) -} - -func TestPushCondition_Match_KindMemberCount_InvalidCondition(t *testing.T) { - condition := newCountPushCondition("foobar") - room := newFakeRoom(1) - assert.False(t, condition.Match(room, countConditionTestEvent)) -} diff --git a/matrix/pushrules/condition_test.go b/matrix/pushrules/condition_test.go deleted file mode 100644 index 163c964..0000000 --- a/matrix/pushrules/condition_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "fmt" - "testing" - - "github.com/stretchr/testify/assert" - "maunium.net/go/mautrix" - "maunium.net/go/gomuks/matrix/pushrules" - "maunium.net/go/gomuks/matrix/rooms" -) - -var ( - blankTestRoom *rooms.Room - displaynameTestRoom pushrules.Room - - countConditionTestEvent *mautrix.Event - - displaynamePushCondition *pushrules.PushCondition -) - -func init() { - blankTestRoom = rooms.NewRoom("!fakeroom:maunium.net", "@tulir:maunium.net") - - countConditionTestEvent = &mautrix.Event{ - Sender: "@tulir:maunium.net", - Type: mautrix.EventMessage, - Timestamp: 1523791120, - ID: "$123:maunium.net", - RoomID: "!fakeroom:maunium.net", - Content: mautrix.Content{ - MsgType: mautrix.MsgText, - Body: "test", - }, - } - - displaynameTestRoom = newFakeRoom(4) - displaynamePushCondition = &pushrules.PushCondition{ - Kind: pushrules.KindContainsDisplayName, - } -} - -func newFakeEvent(evtType mautrix.EventType, content mautrix.Content) *mautrix.Event { - return &mautrix.Event{ - Sender: "@tulir:maunium.net", - Type: evtType, - Timestamp: 1523791120, - ID: "$123:maunium.net", - RoomID: "!fakeroom:maunium.net", - Content: content, - } -} - -func newCountPushCondition(condition string) *pushrules.PushCondition { - return &pushrules.PushCondition{ - Kind: pushrules.KindRoomMemberCount, - MemberCountCondition: condition, - } -} - -func newMatchPushCondition(key, pattern string) *pushrules.PushCondition { - return &pushrules.PushCondition{ - Kind: pushrules.KindEventMatch, - Key: key, - Pattern: pattern, - } -} - -func TestPushCondition_Match_InvalidKind(t *testing.T) { - condition := &pushrules.PushCondition{ - Kind: pushrules.PushCondKind("invalid"), - } - event := newFakeEvent(mautrix.EventType{Type: "m.room.foobar"}, mautrix.Content{}) - assert.False(t, condition.Match(blankTestRoom, event)) -} - -type FakeRoom struct { - members map[string]*mautrix.Member - owner string -} - -func newFakeRoom(memberCount int) *FakeRoom { - room := &FakeRoom{ - owner: "@tulir:maunium.net", - members: make(map[string]*mautrix.Member), - } - - if memberCount >= 1 { - room.members["@tulir:maunium.net"] = &mautrix.Member{ - Membership: mautrix.MembershipJoin, - Displayname: "tulir", - } - } - - for i := 0; i < memberCount-1; i++ { - mxid := fmt.Sprintf("@extrauser_%d:matrix.org", i) - room.members[mxid] = &mautrix.Member{ - Membership: mautrix.MembershipJoin, - Displayname: fmt.Sprintf("Extra User %d", i), - } - } - - return room -} - -func (fr *FakeRoom) GetMember(mxid string) *mautrix.Member { - return fr.members[mxid] -} - -func (fr *FakeRoom) GetSessionOwner() string { - return fr.owner -} - -func (fr *FakeRoom) GetMembers() map[string]*mautrix.Member { - return fr.members -} diff --git a/matrix/pushrules/doc.go b/matrix/pushrules/doc.go deleted file mode 100644 index 19cd774..0000000 --- a/matrix/pushrules/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package pushrules contains utilities to parse push notification rules. -package pushrules diff --git a/matrix/pushrules/pushrules.go b/matrix/pushrules/pushrules.go deleted file mode 100644 index 643f2f2..0000000 --- a/matrix/pushrules/pushrules.go +++ /dev/null @@ -1,37 +0,0 @@ -package pushrules - -import ( - "encoding/json" - "net/url" - - "maunium.net/go/mautrix" -) - -// GetPushRules returns the push notification rules for the global scope. -func GetPushRules(client *mautrix.Client) (*PushRuleset, error) { - return GetScopedPushRules(client, "global") -} - -// GetScopedPushRules returns the push notification rules for the given scope. -func GetScopedPushRules(client *mautrix.Client, scope string) (resp *PushRuleset, err error) { - u, _ := url.Parse(client.BuildURL("pushrules", scope)) - // client.BuildURL returns the URL without a trailing slash, but the pushrules endpoint requires the slash. - u.Path += "/" - _, err = client.MakeRequest("GET", u.String(), nil, &resp) - return -} - -type contentWithRuleset struct { - Ruleset *PushRuleset `json:"global"` -} - -// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON. -func EventToPushRules(event *mautrix.Event) (*PushRuleset, error) { - content := &contentWithRuleset{} - err := json.Unmarshal(event.Content.VeryRaw, content) - if err != nil { - return nil, err - } - - return content.Ruleset, nil -} diff --git a/matrix/pushrules/pushrules_test.go b/matrix/pushrules/pushrules_test.go deleted file mode 100644 index 1883c97..0000000 --- a/matrix/pushrules/pushrules_test.go +++ /dev/null @@ -1,249 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "encoding/json" - "testing" - - "github.com/stretchr/testify/assert" - "maunium.net/go/mautrix" - "maunium.net/go/gomuks/matrix/pushrules" -) - -func TestEventToPushRules(t *testing.T) { - event := &mautrix.Event{ - Type: mautrix.AccountDataPushRules, - Timestamp: 1523380910, - Content: mautrix.Content{ - VeryRaw: json.RawMessage(JSONExamplePushRules), - }, - } - pushRuleset, err := pushrules.EventToPushRules(event) - assert.Nil(t, err) - assert.NotNil(t, pushRuleset) - - assert.IsType(t, pushRuleset.Override, pushrules.PushRuleArray{}) - assert.IsType(t, pushRuleset.Content, pushrules.PushRuleArray{}) - assert.IsType(t, pushRuleset.Room, pushrules.PushRuleMap{}) - assert.IsType(t, pushRuleset.Sender, pushrules.PushRuleMap{}) - assert.IsType(t, pushRuleset.Underride, pushrules.PushRuleArray{}) - assert.Len(t, pushRuleset.Override, 2) - assert.Len(t, pushRuleset.Content, 1) - assert.Empty(t, pushRuleset.Room.Map) - assert.Empty(t, pushRuleset.Sender.Map) - assert.Len(t, pushRuleset.Underride, 6) - - assert.Len(t, pushRuleset.Content[0].Actions, 3) - assert.True(t, pushRuleset.Content[0].Default) - assert.True(t, pushRuleset.Content[0].Enabled) - assert.Empty(t, pushRuleset.Content[0].Conditions) - assert.Equal(t, "alice", pushRuleset.Content[0].Pattern) - assert.Equal(t, ".m.rule.contains_user_name", pushRuleset.Content[0].RuleID) - - assert.False(t, pushRuleset.Override[0].Actions.Should().Notify) - assert.True(t, pushRuleset.Override[0].Actions.Should().NotifySpecified) -} - -const JSONExamplePushRules = `{ - "global": { - "content": [ - { - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "default" - }, - { - "set_tweak": "highlight" - } - ], - "default": true, - "enabled": true, - "pattern": "alice", - "rule_id": ".m.rule.contains_user_name" - } - ], - "override": [ - { - "actions": [ - "dont_notify" - ], - "conditions": [], - "default": true, - "enabled": false, - "rule_id": ".m.rule.master" - }, - { - "actions": [ - "dont_notify" - ], - "conditions": [ - { - "key": "content.msgtype", - "kind": "event_match", - "pattern": "m.notice" - } - ], - "default": true, - "enabled": true, - "rule_id": ".m.rule.suppress_notices" - } - ], - "room": [], - "sender": [], - "underride": [ - { - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "ring" - }, - { - "set_tweak": "highlight", - "value": false - } - ], - "conditions": [ - { - "key": "type", - "kind": "event_match", - "pattern": "m.call.invite" - } - ], - "default": true, - "enabled": true, - "rule_id": ".m.rule.call" - }, - { - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "default" - }, - { - "set_tweak": "highlight" - } - ], - "conditions": [ - { - "kind": "contains_display_name" - } - ], - "default": true, - "enabled": true, - "rule_id": ".m.rule.contains_display_name" - }, - { - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "default" - }, - { - "set_tweak": "highlight", - "value": false - } - ], - "conditions": [ - { - "is": "2", - "kind": "room_member_count" - } - ], - "default": true, - "enabled": true, - "rule_id": ".m.rule.room_one_to_one" - }, - { - "actions": [ - "notify", - { - "set_tweak": "sound", - "value": "default" - }, - { - "set_tweak": "highlight", - "value": false - } - ], - "conditions": [ - { - "key": "type", - "kind": "event_match", - "pattern": "m.room.member" - }, - { - "key": "content.membership", - "kind": "event_match", - "pattern": "invite" - }, - { - "key": "state_key", - "kind": "event_match", - "pattern": "@alice:example.com" - } - ], - "default": true, - "enabled": true, - "rule_id": ".m.rule.invite_for_me" - }, - { - "actions": [ - "notify", - { - "set_tweak": "highlight", - "value": false - } - ], - "conditions": [ - { - "key": "type", - "kind": "event_match", - "pattern": "m.room.member" - } - ], - "default": true, - "enabled": true, - "rule_id": ".m.rule.member_event" - }, - { - "actions": [ - "notify", - { - "set_tweak": "highlight", - "value": false - } - ], - "conditions": [ - { - "key": "type", - "kind": "event_match", - "pattern": "m.room.message" - } - ], - "default": true, - "enabled": true, - "rule_id": ".m.rule.message" - } - ] - } -}` diff --git a/matrix/pushrules/rule.go b/matrix/pushrules/rule.go deleted file mode 100644 index ef43721..0000000 --- a/matrix/pushrules/rule.go +++ /dev/null @@ -1,160 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules - -import ( - "encoding/gob" - - "maunium.net/go/mautrix" - - "maunium.net/go/gomuks/lib/glob" -) - -func init() { - gob.Register(PushRuleArray{}) - gob.Register(PushRuleMap{}) -} - -type PushRuleCollection interface { - GetActions(room Room, event *mautrix.Event) PushActionArray -} - -type PushRuleArray []*PushRule - -func (rules PushRuleArray) SetType(typ PushRuleType) PushRuleArray { - for _, rule := range rules { - rule.Type = typ - } - return rules -} - -func (rules PushRuleArray) GetActions(room Room, event *mautrix.Event) PushActionArray { - for _, rule := range rules { - if !rule.Match(room, event) { - continue - } - return rule.Actions - } - return nil -} - -type PushRuleMap struct { - Map map[string]*PushRule - Type PushRuleType -} - -func (rules PushRuleArray) SetTypeAndMap(typ PushRuleType) PushRuleMap { - data := PushRuleMap{ - Map: make(map[string]*PushRule), - Type: typ, - } - for _, rule := range rules { - rule.Type = typ - data.Map[rule.RuleID] = rule - } - return data -} - -func (ruleMap PushRuleMap) GetActions(room Room, event *mautrix.Event) PushActionArray { - var rule *PushRule - var found bool - switch ruleMap.Type { - case RoomRule: - rule, found = ruleMap.Map[event.RoomID] - case SenderRule: - rule, found = ruleMap.Map[event.Sender] - } - if found && rule.Match(room, event) { - return rule.Actions - } - return nil -} - -func (ruleMap PushRuleMap) Unmap() PushRuleArray { - array := make(PushRuleArray, len(ruleMap.Map)) - index := 0 - for _, rule := range ruleMap.Map { - array[index] = rule - index++ - } - return array -} - -type PushRuleType string - -const ( - OverrideRule PushRuleType = "override" - ContentRule PushRuleType = "content" - RoomRule PushRuleType = "room" - SenderRule PushRuleType = "sender" - UnderrideRule PushRuleType = "underride" -) - -type PushRule struct { - // The type of this rule. - Type PushRuleType `json:"-"` - // The ID of this rule. - // For room-specific rules and user-specific rules, this is the room or user ID (respectively) - // For other types of rules, this doesn't affect anything. - RuleID string `json:"rule_id"` - // The actions this rule should trigger when matched. - Actions PushActionArray `json:"actions"` - // Whether this is a default rule, or has been set explicitly. - Default bool `json:"default"` - // Whether or not this push rule is enabled. - Enabled bool `json:"enabled"` - // The conditions to match in order to trigger this rule. - // Only applicable to generic underride/override rules. - Conditions []*PushCondition `json:"conditions,omitempty"` - // Pattern for content-specific push rules - Pattern string `json:"pattern,omitempty"` -} - -func (rule *PushRule) Match(room Room, event *mautrix.Event) bool { - if !rule.Enabled { - return false - } - switch rule.Type { - case OverrideRule, UnderrideRule: - return rule.matchConditions(room, event) - case ContentRule: - return rule.matchPattern(room, event) - case RoomRule: - return rule.RuleID == event.RoomID - case SenderRule: - return rule.RuleID == event.Sender - default: - return false - } -} - -func (rule *PushRule) matchConditions(room Room, event *mautrix.Event) bool { - for _, cond := range rule.Conditions { - if !cond.Match(room, event) { - return false - } - } - return true -} - -func (rule *PushRule) matchPattern(room Room, event *mautrix.Event) bool { - pattern, err := glob.Compile(rule.Pattern) - if err != nil { - return false - } - return pattern.MatchString(event.Content.Body) -} diff --git a/matrix/pushrules/rule_array_test.go b/matrix/pushrules/rule_array_test.go deleted file mode 100644 index 8bfc5e9..0000000 --- a/matrix/pushrules/rule_array_test.go +++ /dev/null @@ -1,294 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "github.com/stretchr/testify/assert" - "maunium.net/go/gomuks/matrix/pushrules" - "maunium.net/go/mautrix" - "testing" -) - -func TestPushRuleArray_GetActions_FirstMatchReturns(t *testing.T) { - cond1 := newMatchPushCondition("content.msgtype", "m.emote") - cond2 := newMatchPushCondition("content.body", "no match") - actions1 := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "ping"}, - } - rule1 := &pushrules.PushRule{ - Type: pushrules.OverrideRule, - Enabled: true, - Conditions: []*pushrules.PushCondition{cond1, cond2}, - Actions: actions1, - } - - actions2 := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "pong"}, - } - rule2 := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!fakeroom:maunium.net", - Actions: actions2, - } - - actions3 := pushrules.PushActionArray{ - {Action: pushrules.ActionCoalesce}, - } - rule3 := &pushrules.PushRule{ - Type: pushrules.SenderRule, - Enabled: true, - RuleID: "@tulir:maunium.net", - Actions: actions3, - } - - rules := pushrules.PushRuleArray{rule1, rule2, rule3} - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.Equal(t, rules.GetActions(blankTestRoom, event), actions2) -} - -func TestPushRuleArray_GetActions_NoMatchesIsNil(t *testing.T) { - cond1 := newMatchPushCondition("content.msgtype", "m.emote") - cond2 := newMatchPushCondition("content.body", "no match") - actions1 := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "ping"}, - } - rule1 := &pushrules.PushRule{ - Type: pushrules.OverrideRule, - Enabled: true, - Conditions: []*pushrules.PushCondition{cond1, cond2}, - Actions: actions1, - } - - actions2 := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "pong"}, - } - rule2 := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!realroom:maunium.net", - Actions: actions2, - } - - actions3 := pushrules.PushActionArray{ - {Action: pushrules.ActionCoalesce}, - } - rule3 := &pushrules.PushRule{ - Type: pushrules.SenderRule, - Enabled: true, - RuleID: "@otheruser:maunium.net", - Actions: actions3, - } - - rules := pushrules.PushRuleArray{rule1, rule2, rule3} - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.Nil(t, rules.GetActions(blankTestRoom, event)) -} - -func TestPushRuleMap_GetActions_RoomRuleExists(t *testing.T) { - actions1 := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "pong"}, - } - rule1 := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!realroom:maunium.net", - Actions: actions1, - } - - actions2 := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - } - rule2 := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!thirdroom:maunium.net", - Actions: actions2, - } - - actions3 := pushrules.PushActionArray{ - {Action: pushrules.ActionCoalesce}, - } - rule3 := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!fakeroom:maunium.net", - Actions: actions3, - } - - rules := pushrules.PushRuleMap{ - Map: map[string]*pushrules.PushRule{ - rule1.RuleID: rule1, - rule2.RuleID: rule2, - rule3.RuleID: rule3, - }, - Type: pushrules.RoomRule, - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.Equal(t, rules.GetActions(blankTestRoom, event), actions3) -} - -func TestPushRuleMap_GetActions_RoomRuleDoesntExist(t *testing.T) { - actions1 := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "pong"}, - } - rule1 := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!realroom:maunium.net", - Actions: actions1, - } - - actions2 := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - } - rule2 := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!thirdroom:maunium.net", - Actions: actions2, - } - - rules := pushrules.PushRuleMap{ - Map: map[string]*pushrules.PushRule{ - rule1.RuleID: rule1, - rule2.RuleID: rule2, - }, - Type: pushrules.RoomRule, - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.Nil(t, rules.GetActions(blankTestRoom, event)) -} - -func TestPushRuleMap_GetActions_SenderRuleExists(t *testing.T) { - actions1 := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "pong"}, - } - rule1 := &pushrules.PushRule{ - Type: pushrules.SenderRule, - Enabled: true, - RuleID: "@tulir:maunium.net", - Actions: actions1, - } - - actions2 := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - } - rule2 := &pushrules.PushRule{ - Type: pushrules.SenderRule, - Enabled: true, - RuleID: "@someone:maunium.net", - Actions: actions2, - } - - actions3 := pushrules.PushActionArray{ - {Action: pushrules.ActionCoalesce}, - } - rule3 := &pushrules.PushRule{ - Type: pushrules.SenderRule, - Enabled: true, - RuleID: "@otheruser:matrix.org", - Actions: actions3, - } - - rules := pushrules.PushRuleMap{ - Map: map[string]*pushrules.PushRule{ - rule1.RuleID: rule1, - rule2.RuleID: rule2, - rule3.RuleID: rule3, - }, - Type: pushrules.SenderRule, - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.Equal(t, rules.GetActions(blankTestRoom, event), actions1) -} - -func TestPushRuleArray_SetTypeAndMap(t *testing.T) { - actions1 := pushrules.PushActionArray{ - {Action: pushrules.ActionDontNotify}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakHighlight, Value: false}, - {Action: pushrules.ActionSetTweak, Tweak: pushrules.TweakSound, Value: "pong"}, - } - rule1 := &pushrules.PushRule{ - Enabled: true, - RuleID: "@tulir:maunium.net", - Actions: actions1, - } - - actions2 := pushrules.PushActionArray{ - {Action: pushrules.ActionNotify}, - } - rule2 := &pushrules.PushRule{ - Enabled: true, - RuleID: "@someone:maunium.net", - Actions: actions2, - } - - actions3 := pushrules.PushActionArray{ - {Action: pushrules.ActionCoalesce}, - } - rule3 := &pushrules.PushRule{ - Enabled: true, - RuleID: "@otheruser:matrix.org", - Actions: actions3, - } - - ruleArray := pushrules.PushRuleArray{rule1, rule2, rule3} - ruleMap := ruleArray.SetTypeAndMap(pushrules.SenderRule) - assert.Equal(t, pushrules.SenderRule, ruleMap.Type) - for _, rule := range ruleArray { - assert.Equal(t, rule, ruleMap.Map[rule.RuleID]) - } - newRuleArray := ruleMap.Unmap() - for _, rule := range ruleArray { - assert.Contains(t, newRuleArray, rule) - } -} diff --git a/matrix/pushrules/rule_test.go b/matrix/pushrules/rule_test.go deleted file mode 100644 index 56d48fd..0000000 --- a/matrix/pushrules/rule_test.go +++ /dev/null @@ -1,195 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules_test - -import ( - "github.com/stretchr/testify/assert" - "maunium.net/go/gomuks/matrix/pushrules" - "maunium.net/go/mautrix" - "testing" -) - -func TestPushRule_Match_Conditions(t *testing.T) { - cond1 := newMatchPushCondition("content.msgtype", "m.emote") - cond2 := newMatchPushCondition("content.body", "*pushrules") - rule := &pushrules.PushRule{ - Type: pushrules.OverrideRule, - Enabled: true, - Conditions: []*pushrules.PushCondition{cond1, cond2}, - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - Raw: map[string]interface{}{ - "msgtype": "m.emote", - "body": "is testing pushrules", - }, - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.True(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Conditions_Disabled(t *testing.T) { - cond1 := newMatchPushCondition("content.msgtype", "m.emote") - cond2 := newMatchPushCondition("content.body", "*pushrules") - rule := &pushrules.PushRule{ - Type: pushrules.OverrideRule, - Enabled: false, - Conditions: []*pushrules.PushCondition{cond1, cond2}, - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - Raw: map[string]interface{}{ - "msgtype": "m.emote", - "body": "is testing pushrules", - }, - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.False(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Conditions_FailIfOneFails(t *testing.T) { - cond1 := newMatchPushCondition("content.msgtype", "m.emote") - cond2 := newMatchPushCondition("content.body", "*pushrules") - rule := &pushrules.PushRule{ - Type: pushrules.OverrideRule, - Enabled: true, - Conditions: []*pushrules.PushCondition{cond1, cond2}, - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - Raw: map[string]interface{}{ - "msgtype": "m.text", - "body": "I'm testing pushrules", - }, - MsgType: mautrix.MsgText, - Body: "I'm testing pushrules", - }) - assert.False(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Content(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.ContentRule, - Enabled: true, - Pattern: "is testing*", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is testing pushrules", - }) - assert.True(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Content_Fail(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.ContentRule, - Enabled: true, - Pattern: "is testing*", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is not testing pushrules", - }) - assert.False(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Content_ImplicitGlob(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.ContentRule, - Enabled: true, - Pattern: "testing", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "is not testing pushrules", - }) - assert.True(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Content_IllegalGlob(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.ContentRule, - Enabled: true, - Pattern: "this is not a valid glo[b", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{ - MsgType: mautrix.MsgEmote, - Body: "this is not a valid glob", - }) - assert.False(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Room(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!fakeroom:maunium.net", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{}) - assert.True(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Room_Fail(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "!otherroom:maunium.net", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{}) - assert.False(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Sender(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.SenderRule, - Enabled: true, - RuleID: "@tulir:maunium.net", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{}) - assert.True(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_Sender_Fail(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.RoomRule, - Enabled: true, - RuleID: "@someone:matrix.org", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{}) - assert.False(t, rule.Match(blankTestRoom, event)) -} - -func TestPushRule_Match_UnknownTypeAlwaysFail(t *testing.T) { - rule := &pushrules.PushRule{ - Type: pushrules.PushRuleType("foobar"), - Enabled: true, - RuleID: "@someone:matrix.org", - } - - event := newFakeEvent(mautrix.EventMessage, mautrix.Content{}) - assert.False(t, rule.Match(blankTestRoom, event)) -} diff --git a/matrix/pushrules/ruleset.go b/matrix/pushrules/ruleset.go deleted file mode 100644 index 7ad931a..0000000 --- a/matrix/pushrules/ruleset.go +++ /dev/null @@ -1,98 +0,0 @@ -// gomuks - A terminal Matrix client written in Go. -// Copyright (C) 2019 Tulir Asokan -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see <https://www.gnu.org/licenses/>. - -package pushrules - -import ( - "encoding/json" - - "maunium.net/go/mautrix" -) - -type PushRuleset struct { - Override PushRuleArray - Content PushRuleArray - Room PushRuleMap - Sender PushRuleMap - Underride PushRuleArray -} - -type rawPushRuleset struct { - Override PushRuleArray `json:"override"` - Content PushRuleArray `json:"content"` - Room PushRuleArray `json:"room"` - Sender PushRuleArray `json:"sender"` - Underride PushRuleArray `json:"underride"` -} - -// UnmarshalJSON parses JSON into this PushRuleset. -// -// For override, sender and underride push rule arrays, the type is added -// to each PushRule and the array is used as-is. -// -// For room and sender push rule arrays, the type is added to each PushRule -// and the array is converted to a map with the rule ID as the key and the -// PushRule as the value. -func (rs *PushRuleset) UnmarshalJSON(raw []byte) (err error) { - data := rawPushRuleset{} - err = json.Unmarshal(raw, &data) - if err != nil { - return - } - - rs.Override = data.Override.SetType(OverrideRule) - rs.Content = data.Content.SetType(ContentRule) - rs.Room = data.Room.SetTypeAndMap(RoomRule) - rs.Sender = data.Sender.SetTypeAndMap(SenderRule) - rs.Underride = data.Underride.SetType(UnderrideRule) - return -} - -// MarshalJSON is the reverse of UnmarshalJSON() -func (rs *PushRuleset) MarshalJSON() ([]byte, error) { - data := rawPushRuleset{ - Override: rs.Override, - Content: rs.Content, - Room: rs.Room.Unmap(), - Sender: rs.Sender.Unmap(), - Underride: rs.Underride, - } - return json.Marshal(&data) -} - -// DefaultPushActions is the value returned if none of the rule -// collections in a Ruleset match the event given to GetActions() -var DefaultPushActions = PushActionArray{&PushAction{Action: ActionDontNotify}} - -// GetActions matches the given event against all of the push rule -// collections in this push ruleset in the order of priority as -// specified in spec section 11.12.1.4. -func (rs *PushRuleset) GetActions(room Room, event *mautrix.Event) (match PushActionArray) { - // Add push rule collections to array in priority order - arrays := []PushRuleCollection{rs.Override, rs.Content, rs.Room, rs.Sender, rs.Underride} - // Loop until one of the push rule collections matches the room/event combo. - for _, pra := range arrays { - if pra == nil { - continue - } - if match = pra.GetActions(room, event); match != nil { - // Match found, return it. - return - } - } - // No match found, return default actions. - return DefaultPushActions -} |