2018-03-21 22:29:58 +01:00
|
|
|
// gomuks - A terminal Matrix client written in Go.
|
|
|
|
// Copyright (C) 2018 Tulir Asokan
|
|
|
|
//
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU 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 General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
package pushrules
|
|
|
|
|
|
|
|
import (
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
2018-11-13 23:00:35 +01:00
|
|
|
"maunium.net/go/mautrix"
|
2018-05-22 16:24:47 +02:00
|
|
|
"maunium.net/go/gomuks/lib/glob"
|
2018-03-21 22:29:58 +01:00
|
|
|
)
|
|
|
|
|
2018-04-15 14:36:01 +02:00
|
|
|
// Room is an interface with the functions that are needed for processing room-specific push conditions
|
|
|
|
type Room interface {
|
2018-11-13 23:00:35 +01:00
|
|
|
GetMember(mxid string) *mautrix.Member
|
|
|
|
GetMembers() map[string]*mautrix.Member
|
2018-09-05 09:55:48 +02:00
|
|
|
GetSessionOwner() string
|
2018-04-15 14:36:01 +02:00
|
|
|
}
|
|
|
|
|
2018-03-21 22:29:58 +01:00
|
|
|
// 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.
|
2018-11-13 23:00:35 +01:00
|
|
|
func (cond *PushCondition) Match(room Room, event *mautrix.Event) bool {
|
2018-03-21 22:29:58 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-13 23:00:35 +01:00
|
|
|
func (cond *PushCondition) matchValue(room Room, event *mautrix.Event) bool {
|
2018-03-21 22:29:58 +01:00
|
|
|
index := strings.IndexRune(cond.Key, '.')
|
|
|
|
key := cond.Key
|
|
|
|
subkey := ""
|
|
|
|
if index > 0 {
|
|
|
|
subkey = key[index+1:]
|
|
|
|
key = key[0:index]
|
|
|
|
}
|
|
|
|
|
2018-05-02 21:30:43 +02:00
|
|
|
pattern, err := glob.Compile(cond.Pattern)
|
|
|
|
if err != nil {
|
|
|
|
return false
|
|
|
|
}
|
2018-03-21 22:29:58 +01:00
|
|
|
|
|
|
|
switch key {
|
|
|
|
case "type":
|
2018-09-05 09:55:48 +02:00
|
|
|
return pattern.MatchString(event.Type.String())
|
2018-03-21 22:29:58 +01:00
|
|
|
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":
|
2018-09-05 09:55:48 +02:00
|
|
|
val, _ := event.Content.Raw[subkey].(string)
|
2018-03-21 22:29:58 +01:00
|
|
|
return pattern.MatchString(val)
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-13 23:00:35 +01:00
|
|
|
func (cond *PushCondition) matchDisplayName(room Room, event *mautrix.Event) bool {
|
2018-09-05 09:55:48 +02:00
|
|
|
ownerID := room.GetSessionOwner()
|
|
|
|
if ownerID == event.Sender {
|
2018-03-21 22:29:58 +01:00
|
|
|
return false
|
|
|
|
}
|
2018-09-05 09:55:48 +02:00
|
|
|
member := room.GetMember(ownerID)
|
2018-11-14 00:11:40 +01:00
|
|
|
if member == nil {
|
|
|
|
return false
|
|
|
|
}
|
2018-09-05 09:55:48 +02:00
|
|
|
return strings.Contains(event.Content.Body, member.Displayname)
|
2018-03-21 22:29:58 +01:00
|
|
|
}
|
|
|
|
|
2018-11-13 23:00:35 +01:00
|
|
|
func (cond *PushCondition) matchMemberCount(room Room, event *mautrix.Event) bool {
|
2018-04-15 14:36:01 +02:00
|
|
|
group := MemberCountFilterRegex.FindStringSubmatch(cond.MemberCountCondition)
|
|
|
|
if len(group) != 3 {
|
2018-03-21 22:29:58 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-04-15 14:36:01 +02:00
|
|
|
operator := group[1]
|
|
|
|
wantedMemberCount, _ := strconv.Atoi(group[2])
|
2018-03-21 22:29:58 +01:00
|
|
|
|
|
|
|
memberCount := len(room.GetMembers())
|
|
|
|
|
|
|
|
switch operator {
|
2018-04-15 14:36:01 +02:00
|
|
|
case "==", "":
|
|
|
|
return memberCount == wantedMemberCount
|
2018-03-21 22:29:58 +01:00
|
|
|
case ">":
|
2018-04-15 14:36:01 +02:00
|
|
|
return memberCount > wantedMemberCount
|
2018-03-21 22:29:58 +01:00
|
|
|
case ">=":
|
2018-04-15 14:36:01 +02:00
|
|
|
return memberCount >= wantedMemberCount
|
2018-03-21 22:29:58 +01:00
|
|
|
case "<":
|
2018-04-15 14:36:01 +02:00
|
|
|
return memberCount < wantedMemberCount
|
2018-03-21 22:29:58 +01:00
|
|
|
case "<=":
|
2018-04-15 14:36:01 +02:00
|
|
|
return memberCount <= wantedMemberCount
|
2018-03-21 22:29:58 +01:00
|
|
|
default:
|
2018-04-15 14:36:01 +02:00
|
|
|
// Should be impossible due to regex.
|
2018-03-21 22:29:58 +01:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|