// 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 . package pushrules import ( "regexp" "strconv" "strings" "github.com/zyedidia/glob" "maunium.net/go/gomatrix" "maunium.net/go/gomuks/matrix/room" ) // 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 *rooms.Room, event *gomatrix.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 *rooms.Room, event *gomatrix.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) 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[subkey].(string) return pattern.MatchString(val) default: return false } } func (cond *PushCondition) matchDisplayName(room *rooms.Room, event *gomatrix.Event) bool { member := room.GetMember(room.SessionUserID) if member == nil { return false } text, _ := event.Content["body"].(string) return strings.Contains(text, member.DisplayName) } func (cond *PushCondition) matchMemberCount(room *rooms.Room, event *gomatrix.Event) bool { groupGroups := MemberCountFilterRegex.FindAllStringSubmatch(cond.MemberCountCondition, -1) if len(groupGroups) != 1 { return false } operator := "==" wantedMemberCount := 0 group := groupGroups[0] if len(group) == 0 { return false } else if len(group) == 1 { wantedMemberCount, _ = strconv.Atoi(group[0]) } else { operator = group[0] wantedMemberCount, _ = strconv.Atoi(group[1]) } memberCount := len(room.GetMembers()) switch operator { case "==": return wantedMemberCount == memberCount case ">": return wantedMemberCount > memberCount case ">=": return wantedMemberCount >= memberCount case "<": return wantedMemberCount < memberCount case "<=": return wantedMemberCount <= memberCount default: return false } }