summaryrefslogtreecommitdiffstats
path: root/vendor/maunium.net/go/mautrix/pushrules
diff options
context:
space:
mode:
authormsglm <msglm@techchud.xyz>2023-10-27 07:08:25 -0500
committermsglm <msglm@techchud.xyz>2023-10-27 07:08:25 -0500
commit032a7e0c1188d3507b8d9a9571f2446a43cf775b (patch)
tree2bd38c01bc7761a6195e426082ce7191ebc765a1 /vendor/maunium.net/go/mautrix/pushrules
parent56e7bd01ca09ad52b0c4f48f146a20a4f1b78696 (diff)
downloadmatterbridge-msglm-1.26.0+0.1.0.tar.gz
matterbridge-msglm-1.26.0+0.1.0.tar.bz2
matterbridge-msglm-1.26.0+0.1.0.zip
apply https://github.com/42wim/matterbridge/pull/1864v1.26.0+0.1.0
Diffstat (limited to 'vendor/maunium.net/go/mautrix/pushrules')
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/action.go124
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/condition.go266
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/doc.go2
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/glob/LICENSE22
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/glob/README.md28
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/glob/glob.go108
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/pushrules.go37
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/rule.go154
-rw-r--r--vendor/maunium.net/go/mautrix/pushrules/ruleset.go88
9 files changed, 829 insertions, 0 deletions
diff --git a/vendor/maunium.net/go/mautrix/pushrules/action.go b/vendor/maunium.net/go/mautrix/pushrules/action.go
new file mode 100644
index 00000000..844c3eec
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/action.go
@@ -0,0 +1,124 @@
+// Copyright (c) 2020 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+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/vendor/maunium.net/go/mautrix/pushrules/condition.go b/vendor/maunium.net/go/mautrix/pushrules/condition.go
new file mode 100644
index 00000000..f809f8e7
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/condition.go
@@ -0,0 +1,266 @@
+// Copyright (c) 2022 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+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"
+)
+
+// Room is an interface with the functions that are needed for processing room-specific push conditions
+type Room interface {
+ GetOwnDisplayname() string
+ 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
+
+// The allowed push condition kinds as specified in https://spec.matrix.org/v1.2/client-server-api/#conditions-1
+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.
+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"`
+
+ // 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.
+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, 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:
+ return cond.matchMemberCount(room)
+ default:
+ return false
+ }
+}
+
+func splitWithEscaping(s string, separator, escape byte) []string {
+ var token []byte
+ var tokens []string
+ for i := 0; i < len(s); i++ {
+ if s[i] == separator {
+ tokens = append(tokens, string(token))
+ token = token[:0]
+ } else if s[i] == escape && i+1 < len(s) {
+ i++
+ token = append(token, s[i])
+ } else {
+ token = append(token, s[i])
+ }
+ }
+ tokens = append(tokens, string(token))
+ return tokens
+}
+
+func hackyNestedGet(data map[string]interface{}, path []string) (interface{}, bool) {
+ val, ok := data[path[0]]
+ if len(path) == 1 {
+ // We don't have any more path parts, return the value regardless of whether it exists or not.
+ return val, ok
+ } else if ok {
+ if mapVal, ok := val.(map[string]interface{}); ok {
+ val, ok = hackyNestedGet(mapVal, path[1:])
+ if ok {
+ return val, true
+ }
+ }
+ }
+ // If we don't find the key, try to combine the first two parts.
+ // e.g. if the key is content.m.relates_to.rel_type, we'll first try data["m"], which will fail,
+ // then combine m and relates_to to get data["m.relates_to"], which should succeed.
+ path[1] = path[0] + "." + path[1]
+ return hackyNestedGet(data, path[1:])
+}
+
+func stringifyForPushCondition(val interface{}) string {
+ // Implement MSC3862 to allow matching any type of field
+ // https://github.com/matrix-org/matrix-spec-proposals/pull/3862
+ switch typedVal := val.(type) {
+ case string:
+ return typedVal
+ case nil:
+ return "null"
+ case float64:
+ // Floats aren't allowed in Matrix events, but the JSON parser always stores numbers as floats,
+ // so just handle that and convert to int
+ return strconv.FormatInt(int64(typedVal), 10)
+ default:
+ return fmt.Sprint(val)
+ }
+}
+
+func (cond *PushCondition) matchValue(room Room, evt *event.Event) bool {
+ key, subkey, _ := strings.Cut(cond.Key, ".")
+
+ pattern, err := glob.Compile(cond.Pattern)
+ if err != nil {
+ return false
+ }
+
+ switch key {
+ case "type":
+ return pattern.MatchString(evt.Type.String())
+ case "sender":
+ return pattern.MatchString(string(evt.Sender))
+ case "room_id":
+ return pattern.MatchString(string(evt.RoomID))
+ case "state_key":
+ if evt.StateKey == nil {
+ return false
+ }
+ return pattern.MatchString(*evt.StateKey)
+ case "content":
+ // Split the match key with escaping to implement https://github.com/matrix-org/matrix-spec-proposals/pull/3873
+ splitKey := splitWithEscaping(subkey, '.', '\\')
+ // Then do a hacky nested get that supports combining parts for the backwards-compat part of MSC3873
+ val, ok := hackyNestedGet(evt.Content.Raw, splitKey)
+ if !ok {
+ return cond.Pattern == ""
+ }
+ return pattern.MatchString(stringifyForPushCondition(val))
+ default:
+ return false
+ }
+}
+
+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.Raw), &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 {
+ return false
+ }
+
+ msg, ok := evt.Content.Raw["body"].(string)
+ if !ok {
+ return false
+ }
+
+ isAcceptable := func(r uint8) bool {
+ return unicode.IsSpace(rune(r)) || unicode.IsPunct(rune(r))
+ }
+ length := len(displayname)
+ for index := strings.Index(msg, displayname); index != -1; index = strings.Index(msg, displayname) {
+ if (index <= 0 || isAcceptable(msg[index-1])) && (index+length >= len(msg) || isAcceptable(msg[index+length])) {
+ return true
+ }
+ msg = msg[index+len(displayname):]
+ }
+ return false
+}
+
+func (cond *PushCondition) matchMemberCount(room Room) bool {
+ group := MemberCountFilterRegex.FindStringSubmatch(cond.MemberCountCondition)
+ if len(group) != 3 {
+ return false
+ }
+
+ operator := group[1]
+ wantedMemberCount, _ := strconv.Atoi(group[2])
+
+ memberCount := room.GetMemberCount()
+
+ 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/vendor/maunium.net/go/mautrix/pushrules/doc.go b/vendor/maunium.net/go/mautrix/pushrules/doc.go
new file mode 100644
index 00000000..19cd7745
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/doc.go
@@ -0,0 +1,2 @@
+// Package pushrules contains utilities to parse push notification rules.
+package pushrules
diff --git a/vendor/maunium.net/go/mautrix/pushrules/glob/LICENSE b/vendor/maunium.net/go/mautrix/pushrules/glob/LICENSE
new file mode 100644
index 00000000..cb00d952
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/glob/LICENSE
@@ -0,0 +1,22 @@
+Glob is licensed under the MIT "Expat" License:
+
+Copyright (c) 2016: Zachary Yedidia.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/maunium.net/go/mautrix/pushrules/glob/README.md b/vendor/maunium.net/go/mautrix/pushrules/glob/README.md
new file mode 100644
index 00000000..e2e6c649
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/glob/README.md
@@ -0,0 +1,28 @@
+# String globbing in Go
+
+[![GoDoc](https://godoc.org/github.com/zyedidia/glob?status.svg)](http://godoc.org/github.com/zyedidia/glob)
+
+This package adds support for globs in Go.
+
+It simply converts glob expressions to regexps. I try to follow the standard defined [here](http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13).
+
+# Example
+
+```go
+package main
+
+import "github.com/zyedidia/glob"
+
+func main() {
+ glob, err := glob.Compile("{*.go,*.c}")
+ if err != nil {
+ // Error
+ }
+
+ glob.Match([]byte("test.c")) // true
+ glob.Match([]byte("hello.go")) // true
+ glob.Match([]byte("test.d")) // false
+}
+```
+
+You can call all the same functions on a glob that you can call on a regexp.
diff --git a/vendor/maunium.net/go/mautrix/pushrules/glob/glob.go b/vendor/maunium.net/go/mautrix/pushrules/glob/glob.go
new file mode 100644
index 00000000..c270dbc5
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/glob/glob.go
@@ -0,0 +1,108 @@
+// Package glob provides objects for matching strings with globs
+package glob
+
+import "regexp"
+
+// Glob is a wrapper of *regexp.Regexp.
+// It should contain a glob expression compiled into a regular expression.
+type Glob struct {
+ *regexp.Regexp
+}
+
+// Compile a takes a glob expression as a string and transforms it
+// into a *Glob object (which is really just a regular expression)
+// Compile also returns a possible error.
+func Compile(pattern string) (*Glob, error) {
+ r, err := globToRegex(pattern)
+ return &Glob{r}, err
+}
+
+func globToRegex(glob string) (*regexp.Regexp, error) {
+ regex := ""
+ inGroup := 0
+ inClass := 0
+ firstIndexInClass := -1
+ arr := []byte(glob)
+
+ hasGlobCharacters := false
+
+ for i := 0; i < len(arr); i++ {
+ ch := arr[i]
+
+ switch ch {
+ case '\\':
+ i++
+ if i >= len(arr) {
+ regex += "\\"
+ } else {
+ next := arr[i]
+ switch next {
+ case ',':
+ // Nothing
+ case 'Q', 'E':
+ regex += "\\\\"
+ default:
+ regex += "\\"
+ }
+ regex += string(next)
+ }
+ case '*':
+ if inClass == 0 {
+ regex += ".*"
+ } else {
+ regex += "*"
+ }
+ hasGlobCharacters = true
+ case '?':
+ if inClass == 0 {
+ regex += "."
+ } else {
+ regex += "?"
+ }
+ hasGlobCharacters = true
+ case '[':
+ inClass++
+ firstIndexInClass = i + 1
+ regex += "["
+ hasGlobCharacters = true
+ case ']':
+ inClass--
+ regex += "]"
+ case '.', '(', ')', '+', '|', '^', '$', '@', '%':
+ if inClass == 0 || (firstIndexInClass == i && ch == '^') {
+ regex += "\\"
+ }
+ regex += string(ch)
+ hasGlobCharacters = true
+ case '!':
+ if firstIndexInClass == i {
+ regex += "^"
+ } else {
+ regex += "!"
+ }
+ hasGlobCharacters = true
+ case '{':
+ inGroup++
+ regex += "("
+ hasGlobCharacters = true
+ case '}':
+ inGroup--
+ regex += ")"
+ case ',':
+ if inGroup > 0 {
+ regex += "|"
+ hasGlobCharacters = true
+ } else {
+ regex += ","
+ }
+ default:
+ regex += string(ch)
+ }
+ }
+
+ if hasGlobCharacters {
+ return regexp.Compile("^" + regex + "$")
+ } else {
+ return regexp.Compile(regex)
+ }
+}
diff --git a/vendor/maunium.net/go/mautrix/pushrules/pushrules.go b/vendor/maunium.net/go/mautrix/pushrules/pushrules.go
new file mode 100644
index 00000000..7944299a
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/pushrules.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package pushrules
+
+import (
+ "encoding/gob"
+ "encoding/json"
+ "reflect"
+
+ "maunium.net/go/mautrix/event"
+)
+
+// EventContent represents the content of a m.push_rules account data event.
+// https://spec.matrix.org/v1.2/client-server-api/#mpush_rules
+type EventContent struct {
+ Ruleset *PushRuleset `json:"global"`
+}
+
+func init() {
+ event.TypeMap[event.AccountDataPushRules] = reflect.TypeOf(EventContent{})
+ gob.Register(&EventContent{})
+}
+
+// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON.
+func EventToPushRules(evt *event.Event) (*PushRuleset, error) {
+ content := &EventContent{}
+ err := json.Unmarshal(evt.Content.VeryRaw, content)
+ if err != nil {
+ return nil, err
+ }
+
+ return content.Ruleset, nil
+}
diff --git a/vendor/maunium.net/go/mautrix/pushrules/rule.go b/vendor/maunium.net/go/mautrix/pushrules/rule.go
new file mode 100644
index 00000000..8ce2da77
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/rule.go
@@ -0,0 +1,154 @@
+// Copyright (c) 2020 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package pushrules
+
+import (
+ "encoding/gob"
+
+ "maunium.net/go/mautrix/event"
+ "maunium.net/go/mautrix/id"
+ "maunium.net/go/mautrix/pushrules/glob"
+)
+
+func init() {
+ gob.Register(PushRuleArray{})
+ gob.Register(PushRuleMap{})
+}
+
+type PushRuleCollection interface {
+ GetActions(room Room, evt *event.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, evt *event.Event) PushActionArray {
+ for _, rule := range rules {
+ if !rule.Match(room, evt) {
+ 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, evt *event.Event) PushActionArray {
+ var rule *PushRule
+ var found bool
+ switch ruleMap.Type {
+ case RoomRule:
+ rule, found = ruleMap.Map[string(evt.RoomID)]
+ case SenderRule:
+ rule, found = ruleMap.Map[string(evt.Sender)]
+ }
+ if found && rule.Match(room, evt) {
+ 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, evt *event.Event) bool {
+ if !rule.Enabled {
+ return false
+ }
+ switch rule.Type {
+ case OverrideRule, UnderrideRule:
+ return rule.matchConditions(room, evt)
+ case ContentRule:
+ return rule.matchPattern(room, evt)
+ case RoomRule:
+ return id.RoomID(rule.RuleID) == evt.RoomID
+ case SenderRule:
+ return id.UserID(rule.RuleID) == evt.Sender
+ default:
+ return false
+ }
+}
+
+func (rule *PushRule) matchConditions(room Room, evt *event.Event) bool {
+ for _, cond := range rule.Conditions {
+ if !cond.Match(room, evt) {
+ return false
+ }
+ }
+ return true
+}
+
+func (rule *PushRule) matchPattern(room Room, evt *event.Event) bool {
+ pattern, err := glob.Compile(rule.Pattern)
+ if err != nil {
+ return false
+ }
+ msg, ok := evt.Content.Raw["body"].(string)
+ if !ok {
+ return false
+ }
+ return pattern.MatchString(msg)
+}
diff --git a/vendor/maunium.net/go/mautrix/pushrules/ruleset.go b/vendor/maunium.net/go/mautrix/pushrules/ruleset.go
new file mode 100644
index 00000000..48ae1e35
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/pushrules/ruleset.go
@@ -0,0 +1,88 @@
+// Copyright (c) 2020 Tulir Asokan
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+package pushrules
+
+import (
+ "encoding/json"
+
+ "maunium.net/go/mautrix/event"
+)
+
+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, evt *event.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, evt); match != nil {
+ // Match found, return it.
+ return
+ }
+ }
+ // No match found, return default actions.
+ return DefaultPushActions
+}