gomuks/coverage.html

683 lines
26 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }
</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">
<option value="file0">maunium.net/go/gomuks/matrix/pushrules/action.go (51.6%)</option>
<option value="file1">maunium.net/go/gomuks/matrix/pushrules/condition.go (97.5%)</option>
<option value="file2">maunium.net/go/gomuks/matrix/pushrules/pushrules.go (50.0%)</option>
<option value="file3">maunium.net/go/gomuks/matrix/pushrules/rule.go (13.6%)</option>
<option value="file4">maunium.net/go/gomuks/matrix/pushrules/ruleset.go (52.9%)</option>
</select>
</div>
<div id="legend">
<span>not tracked</span>
<span class="cov0">not covered</span>
<span class="cov8">covered</span>
</div>
</div>
<div id="content">
<pre class="file" id="file0" style="display: none">// 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 &lt;http://www.gnu.org/licenses/&gt;.
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) <span class="cov8" title="1">{
for _, action := range actions </span><span class="cov8" title="1">{
switch action.Action </span>{
case ActionNotify, ActionCoalesce:<span class="cov0" title="0">
should.Notify = true
should.NotifySpecified = true</span>
case ActionDontNotify:<span class="cov8" title="1">
should.Notify = false
should.NotifySpecified = true</span>
case ActionSetTweak:<span class="cov0" title="0">
switch action.Tweak </span>{
case TweakHighlight:<span class="cov0" title="0">
var ok bool
should.Highlight, ok = action.Value.(bool)
if !ok </span><span class="cov0" title="0">{
// Highlight value not specified, so assume true since the tweak is set.
should.Highlight = true
}</span>
case TweakSound:<span class="cov0" title="0">
should.SoundName = action.Value.(string)
should.PlaySound = len(should.SoundName) &gt; 0</span>
}
}
}
<span class="cov8" title="1">return</span>
}
// 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 <span class="cov8" title="1">{
var data interface{}
err := json.Unmarshal(raw, &amp;data)
if err != nil </span><span class="cov0" title="0">{
return err
}</span>
<span class="cov8" title="1">switch val := data.(type) </span>{
case string:<span class="cov8" title="1">
action.Action = PushActionType(val)</span>
case map[string]interface{}:<span class="cov8" title="1">
tweak, ok := val["set_tweak"].(string)
if ok </span><span class="cov8" title="1">{
action.Action = ActionSetTweak
action.Tweak = PushActionTweak(tweak)
action.Value, _ = val["value"]
}</span>
}
<span class="cov8" title="1">return nil</span>
}
// MarshalJSON is the reverse of UnmarshalJSON()
func (action *PushAction) MarshalJSON() (raw []byte, err error) <span class="cov0" title="0">{
if action.Action == ActionSetTweak </span><span class="cov0" title="0">{
data := map[string]interface{}{
"set_tweak": action.Tweak,
"value": action.Value,
}
return json.Marshal(&amp;data)
}</span><span class="cov0" title="0"> else {
data := string(action.Action)
return json.Marshal(&amp;data)
}</span>
}
</pre>
<pre class="file" id="file1" style="display: none">// 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 &lt;http://www.gnu.org/licenses/&gt;.
package pushrules
import (
"regexp"
"strconv"
"strings"
"github.com/zyedidia/glob"
"maunium.net/go/gomatrix"
"maunium.net/go/gomuks/matrix/rooms"
)
// 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() *rooms.Member
}
// 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 ==, &lt;, &gt;, &gt;= or &lt;=. 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("^(==|[&lt;&gt;]=?)?([0-9]+)$")
// Match checks if this condition is fulfilled for the given event in the given room.
func (cond *PushCondition) Match(room Room, event *gomatrix.Event) bool <span class="cov8" title="1">{
switch cond.Kind </span>{
case KindEventMatch:<span class="cov8" title="1">
return cond.matchValue(room, event)</span>
case KindContainsDisplayName:<span class="cov8" title="1">
return cond.matchDisplayName(room, event)</span>
case KindRoomMemberCount:<span class="cov8" title="1">
return cond.matchMemberCount(room, event)</span>
default:<span class="cov8" title="1">
return false</span>
}
}
func (cond *PushCondition) matchValue(room Room, event *gomatrix.Event) bool <span class="cov8" title="1">{
index := strings.IndexRune(cond.Key, '.')
key := cond.Key
subkey := ""
if index &gt; 0 </span><span class="cov8" title="1">{
subkey = key[index+1:]
key = key[0:index]
}</span>
<span class="cov8" title="1">pattern, _ := glob.Compile(cond.Pattern)
switch key </span>{
case "type":<span class="cov8" title="1">
return pattern.MatchString(event.Type)</span>
case "sender":<span class="cov8" title="1">
return pattern.MatchString(event.Sender)</span>
case "room_id":<span class="cov8" title="1">
return pattern.MatchString(event.RoomID)</span>
case "state_key":<span class="cov8" title="1">
if event.StateKey == nil </span><span class="cov8" title="1">{
return cond.Pattern == ""
}</span>
<span class="cov8" title="1">return pattern.MatchString(*event.StateKey)</span>
case "content":<span class="cov8" title="1">
val, _ := event.Content[subkey].(string)
return pattern.MatchString(val)</span>
default:<span class="cov8" title="1">
return false</span>
}
}
func (cond *PushCondition) matchDisplayName(room Room, event *gomatrix.Event) bool <span class="cov8" title="1">{
member := room.GetSessionOwner()
if member == nil || member.UserID == event.Sender </span><span class="cov8" title="1">{
return false
}</span>
<span class="cov8" title="1">text, _ := event.Content["body"].(string)
return strings.Contains(text, member.DisplayName)</span>
}
func (cond *PushCondition) matchMemberCount(room Room, event *gomatrix.Event) bool <span class="cov8" title="1">{
group := MemberCountFilterRegex.FindStringSubmatch(cond.MemberCountCondition)
if len(group) != 3 </span><span class="cov8" title="1">{
return false
}</span>
<span class="cov8" title="1">operator := group[1]
wantedMemberCount, _ := strconv.Atoi(group[2])
memberCount := len(room.GetMembers())
switch operator </span>{
case "==", "":<span class="cov8" title="1">
return memberCount == wantedMemberCount</span>
case "&gt;":<span class="cov8" title="1">
return memberCount &gt; wantedMemberCount</span>
case "&gt;=":<span class="cov8" title="1">
return memberCount &gt;= wantedMemberCount</span>
case "&lt;":<span class="cov8" title="1">
return memberCount &lt; wantedMemberCount</span>
case "&lt;=":<span class="cov8" title="1">
return memberCount &lt;= wantedMemberCount</span>
default:<span class="cov0" title="0">
// Should be impossible due to regex.
return false</span>
}
}
</pre>
<pre class="file" id="file2" style="display: none">package pushrules
import (
"encoding/json"
"net/url"
"maunium.net/go/gomatrix"
)
// GetPushRules returns the push notification rules for the global scope.
func GetPushRules(client *gomatrix.Client) (*PushRuleset, error) <span class="cov0" title="0">{
return GetScopedPushRules(client, "global")
}</span>
// GetScopedPushRules returns the push notification rules for the given scope.
func GetScopedPushRules(client *gomatrix.Client, scope string) (resp *PushRuleset, err error) <span class="cov0" title="0">{
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, &amp;resp)
return
}</span>
// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON.
func EventToPushRules(event *gomatrix.Event) (*PushRuleset, error) <span class="cov8" title="1">{
content, _ := event.Content["global"]
raw, err := json.Marshal(content)
if err != nil </span><span class="cov0" title="0">{
return nil, err
}</span>
<span class="cov8" title="1">ruleset := &amp;PushRuleset{}
err = json.Unmarshal(raw, ruleset)
if err != nil </span><span class="cov0" title="0">{
return nil, err
}</span>
<span class="cov8" title="1">return ruleset, nil</span>
}
</pre>
<pre class="file" id="file3" style="display: none">// 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 &lt;http://www.gnu.org/licenses/&gt;.
package pushrules
import (
"github.com/zyedidia/glob"
"maunium.net/go/gomatrix"
)
type PushRuleCollection interface {
GetActions(room Room, event *gomatrix.Event) PushActionArray
}
type PushRuleArray []*PushRule
func (rules PushRuleArray) setType(typ PushRuleType) PushRuleArray <span class="cov8" title="1">{
for _, rule := range rules </span><span class="cov8" title="1">{
rule.Type = typ
}</span>
<span class="cov8" title="1">return rules</span>
}
func (rules PushRuleArray) GetActions(room Room, event *gomatrix.Event) PushActionArray <span class="cov0" title="0">{
for _, rule := range rules </span><span class="cov0" title="0">{
if !rule.Match(room, event) </span><span class="cov0" title="0">{
continue</span>
}
<span class="cov0" title="0">return rule.Actions</span>
}
<span class="cov0" title="0">return nil</span>
}
type PushRuleMap struct {
Map map[string]*PushRule
Type PushRuleType
}
func (rules PushRuleArray) setTypeAndMap(typ PushRuleType) PushRuleMap <span class="cov8" title="1">{
data := PushRuleMap{
Map: make(map[string]*PushRule),
Type: typ,
}
for _, rule := range rules </span><span class="cov0" title="0">{
rule.Type = typ
data.Map[rule.RuleID] = rule
}</span>
<span class="cov8" title="1">return data</span>
}
func (ruleMap PushRuleMap) GetActions(room Room, event *gomatrix.Event) PushActionArray <span class="cov0" title="0">{
var rule *PushRule
var found bool
switch ruleMap.Type </span>{
case RoomRule:<span class="cov0" title="0">
rule, found = ruleMap.Map[event.RoomID]</span>
case SenderRule:<span class="cov0" title="0">
rule, found = ruleMap.Map[event.Sender]</span>
}
<span class="cov0" title="0">if found &amp;&amp; rule.Match(room, event) </span><span class="cov0" title="0">{
return rule.Actions
}</span>
<span class="cov0" title="0">return nil</span>
}
func (ruleMap PushRuleMap) unmap() PushRuleArray <span class="cov0" title="0">{
array := make(PushRuleArray, len(ruleMap.Map))
index := 0
for _, rule := range ruleMap.Map </span><span class="cov0" title="0">{
array[index] = rule
index++
}</span>
<span class="cov0" title="0">return array</span>
}
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 *gomatrix.Event) bool <span class="cov0" title="0">{
if !rule.Enabled </span><span class="cov0" title="0">{
return false
}</span>
<span class="cov0" title="0">switch rule.Type </span>{
case OverrideRule, UnderrideRule:<span class="cov0" title="0">
return rule.matchConditions(room, event)</span>
case ContentRule:<span class="cov0" title="0">
return rule.matchPattern(room, event)</span>
case RoomRule:<span class="cov0" title="0">
return rule.RuleID == event.RoomID</span>
case SenderRule:<span class="cov0" title="0">
return rule.RuleID == event.Sender</span>
default:<span class="cov0" title="0">
return false</span>
}
}
func (rule *PushRule) matchConditions(room Room, event *gomatrix.Event) bool <span class="cov0" title="0">{
for _, cond := range rule.Conditions </span><span class="cov0" title="0">{
if !cond.Match(room, event) </span><span class="cov0" title="0">{
return false
}</span>
}
<span class="cov0" title="0">return true</span>
}
func (rule *PushRule) matchPattern(room Room, event *gomatrix.Event) bool <span class="cov0" title="0">{
pattern, err := glob.Compile(rule.Pattern)
if err != nil </span><span class="cov0" title="0">{
return false
}</span>
<span class="cov0" title="0">text, _ := event.Content["body"].(string)
return pattern.MatchString(text)</span>
}
</pre>
<pre class="file" id="file4" style="display: none">// 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 &lt;http://www.gnu.org/licenses/&gt;.
package pushrules
import (
"encoding/json"
"maunium.net/go/gomatrix"
)
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) <span class="cov8" title="1">{
data := rawPushRuleset{}
err = json.Unmarshal(raw, &amp;data)
if err != nil </span><span class="cov0" title="0">{
return
}</span>
<span class="cov8" title="1">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</span>
}
// MarshalJSON is the reverse of UnmarshalJSON()
func (rs *PushRuleset) MarshalJSON() ([]byte, error) <span class="cov0" title="0">{
data := rawPushRuleset{
Override: rs.Override,
Content: rs.Content,
Room: rs.Room.unmap(),
Sender: rs.Sender.unmap(),
Underride: rs.Underride,
}
return json.Marshal(&amp;data)
}</span>
// DefaultPushActions is the value returned if none of the rule
// collections in a Ruleset match the event given to GetActions()
var DefaultPushActions = make(PushActionArray, 0)
// 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 *gomatrix.Event) (match PushActionArray) <span class="cov0" title="0">{
// 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 </span><span class="cov0" title="0">{
if match = pra.GetActions(room, event); match != nil </span><span class="cov0" title="0">{
// Match found, return it.
return
}</span>
}
// No match found, return default actions.
<span class="cov0" title="0">return DefaultPushActions</span>
}
</pre>
</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>