aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--config/config.go4
-rw-r--r--config/session.go16
-rw-r--r--gomuks.go6
-rw-r--r--matrix/ext/doc.go18
-rw-r--r--matrix/ext/pushrules.go438
-rw-r--r--matrix/matrix.go51
-rw-r--r--matrix/pushrules/action.go135
-rw-r--r--matrix/pushrules/condition.go147
-rw-r--r--matrix/pushrules/doc.go2
-rw-r--r--matrix/pushrules/pushrules.go42
-rw-r--r--matrix/pushrules/rule.go150
-rw-r--r--matrix/pushrules/ruleset.go87
-rw-r--r--matrix/room/member.go28
-rw-r--r--matrix/room/room.go17
-rw-r--r--matrix/sync.go32
-rw-r--r--ui/view-login.go2
-rw-r--r--ui/view-main.go2
17 files changed, 673 insertions, 504 deletions
diff --git a/config/config.go b/config/config.go
index 858f1d2..7d99274 100644
--- a/config/config.go
+++ b/config/config.go
@@ -27,8 +27,8 @@ import (
)
type Config struct {
- MXID string `yaml:"mxid"`
- HS string `yaml:"homeserver"`
+ UserID string `yaml:"mxid"`
+ HS string `yaml:"homeserver"`
dir string `yaml:"-"`
Session *Session `yaml:"-"`
diff --git a/config/session.go b/config/session.go
index 631c954..3fdc169 100644
--- a/config/session.go
+++ b/config/session.go
@@ -22,19 +22,19 @@ import (
"path/filepath"
"maunium.net/go/gomatrix"
- "maunium.net/go/gomuks/matrix/ext"
+ "maunium.net/go/gomuks/matrix/pushrules"
rooms "maunium.net/go/gomuks/matrix/room"
"maunium.net/go/gomuks/ui/debug"
)
type Session struct {
- MXID string `json:"-"`
+ UserID string `json:"-"`
path string `json:"-"`
AccessToken string
NextBatch string
FilterID string
Rooms map[string]*rooms.Room
- PushRules *gomx_ext.PushRuleset
+ PushRules *pushrules.PushRuleset
}
func (config *Config) LoadSession(mxid string) error {
@@ -44,9 +44,9 @@ func (config *Config) LoadSession(mxid string) error {
func (config *Config) NewSession(mxid string) *Session {
return &Session{
- MXID: mxid,
- path: filepath.Join(config.dir, mxid+".session"),
- Rooms: make(map[string]*rooms.Room),
+ UserID: mxid,
+ path: filepath.Join(config.dir, mxid+".session"),
+ Rooms: make(map[string]*rooms.Room),
}
}
@@ -76,7 +76,7 @@ func (s *Session) Load() error {
func (s *Session) Save() error {
data, err := json.Marshal(s)
if err != nil {
- debug.Print("Failed to marshal session of", s.MXID, err)
+ debug.Print("Failed to marshal session of", s.UserID, err)
return err
}
@@ -99,7 +99,7 @@ func (s *Session) LoadNextBatch(_ string) string {
func (s *Session) GetRoom(mxid string) *rooms.Room {
room, _ := s.Rooms[mxid]
if room == nil {
- room = rooms.NewRoom(mxid, s.MXID)
+ room = rooms.NewRoom(mxid, s.UserID)
s.Rooms[room.ID] = room
}
return room
diff --git a/gomuks.go b/gomuks.go
index b4c74fe..6429f24 100644
--- a/gomuks.go
+++ b/gomuks.go
@@ -54,11 +54,11 @@ func NewGomuks(enableDebug bool) *Gomuks {
gmx.config = config.NewConfig(configDir)
gmx.ui = ui.NewGomuksUI(gmx)
- gmx.matrix = matrix.NewMatrixContainer(gmx)
+ gmx.matrix = matrix.NewContainer(gmx)
gmx.config.Load()
- if len(gmx.config.MXID) > 0 {
- gmx.config.LoadSession(gmx.config.MXID)
+ if len(gmx.config.UserID) > 0 {
+ gmx.config.LoadSession(gmx.config.UserID)
}
gmx.matrix.InitClient()
diff --git a/matrix/ext/doc.go b/matrix/ext/doc.go
deleted file mode 100644
index 21b818c..0000000
--- a/matrix/ext/doc.go
+++ /dev/null
@@ -1,18 +0,0 @@
-// 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 gomx_ext contains extensions to the gomatrix package.
-package gomx_ext
diff --git a/matrix/ext/pushrules.go b/matrix/ext/pushrules.go
deleted file mode 100644
index ccccbb0..0000000
--- a/matrix/ext/pushrules.go
+++ /dev/null
@@ -1,438 +0,0 @@
-package gomx_ext
-
-import (
- "encoding/json"
- "net/url"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/zyedidia/glob"
- "maunium.net/go/gomatrix"
- "maunium.net/go/gomuks/matrix/room"
-)
-
-// GetPushRules returns the push notification rules for the global scope.
-func GetPushRules(client *gomatrix.Client) (*PushRuleset, error) {
- return GetScopedPushRules(client, "global")
-}
-
-// GetScopedPushRules returns the push notification rules for the given scope.
-func GetScopedPushRules(client *gomatrix.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
-}
-
-func EventToPushRules(event *gomatrix.Event) (*PushRuleset, error) {
- content, _ := event.Content["global"]
- raw, err := json.Marshal(content)
- if err != nil {
- return nil, err
- }
-
- ruleset := &PushRuleset{}
- err = json.Unmarshal(raw, ruleset)
- if err != nil {
- return nil, err
- }
-
- return ruleset, nil
-}
-
-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"`
-}
-
-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
-}
-
-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)
-}
-
-var DefaultPushActions = make(PushActionArray, 0)
-
-func (rs *PushRuleset) GetActions(room *rooms.Room, event *gomatrix.Event) (match PushActionArray) {
- if match = rs.Override.GetActions(room, event); match != nil {
- return
- }
- if match = rs.Content.GetActions(room, event); match != nil {
- return
- }
- if match = rs.Room.GetActions(room, event); match != nil {
- return
- }
- if match = rs.Sender.GetActions(room, event); match != nil {
- return
- }
- if match = rs.Underride.GetActions(room, event); match != nil {
- return
- }
- return DefaultPushActions
-}
-
-type PushRuleArray []*PushRule
-
-func (rules PushRuleArray) setType(typ PushRuleType) PushRuleArray {
- for _, rule := range rules {
- rule.Type = typ
- }
- return rules
-}
-
-func (rules PushRuleArray) GetActions(room *rooms.Room, event *gomatrix.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 *rooms.Room, event *gomatrix.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 *rooms.Room, event *gomatrix.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 *rooms.Room, event *gomatrix.Event) bool {
- for _, cond := range rule.Conditions {
- if !cond.Match(room, event) {
- return false
- }
- }
- return true
-}
-
-func (rule *PushRule) matchPattern(room *rooms.Room, event *gomatrix.Event) bool {
- pattern, err := glob.Compile(rule.Pattern)
- if err != nil {
- return false
- }
- text, _ := event.Content["body"].(string)
- return pattern.MatchString(text)
-}
-
-type PushActionType string
-
-const (
- ActionNotify PushActionType = "notify"
- ActionDontNotify PushActionType = "dont_notify"
- ActionCoalesce PushActionType = "coalesce"
- ActionSetTweak PushActionType = "set_tweak"
-)
-
-type PushActionTweak string
-
-const (
- TweakSound PushActionTweak = "sound"
- TweakHighlight PushActionTweak = "highlight"
-)
-
-type PushActionArray []*PushAction
-
-type PushActionArrayShould struct {
- NotifySpecified bool
- Notify bool
- Highlight bool
-
- PlaySound bool
- SoundName string
-}
-
-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
-}
-
-type PushAction struct {
- Action PushActionType
- Tweak PushActionTweak
- Value interface{}
-}
-
-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
-}
-
-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)
- } else {
- data := string(action.Action)
- return json.Marshal(&data)
- }
-}
-
-type PushKind string
-
-const (
- KindEventMatch PushKind = "event_match"
- KindContainsDisplayName PushKind = "contains_display_name"
- KindRoomMemberCount PushKind = "room_member_count"
-)
-
-type PushCondition struct {
- Kind PushKind `json:"kind"`
- Key string `json:"key,omitempty"`
- Pattern string `json:"pattern,omitempty"`
- Is string `json:"string,omitempty"`
-}
-
-var MemberCountFilterRegex = regexp.MustCompile("^(==|[<>]=?)?([0-9]+)$")
-
-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.Owner)
- 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.Is, -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
- }
-}
diff --git a/matrix/matrix.go b/matrix/matrix.go
index 6ab922c..a73e050 100644
--- a/matrix/matrix.go
+++ b/matrix/matrix.go
@@ -25,13 +25,16 @@ import (
"maunium.net/go/gomatrix"
"maunium.net/go/gomuks/config"
"maunium.net/go/gomuks/interface"
- "maunium.net/go/gomuks/matrix/ext"
+ "maunium.net/go/gomuks/matrix/pushrules"
"maunium.net/go/gomuks/matrix/room"
"maunium.net/go/gomuks/notification"
"maunium.net/go/gomuks/ui/debug"
"maunium.net/go/gomuks/ui/widget"
)
+// Container is a wrapper for a gomatrix Client and some other stuff.
+//
+// It is used for all Matrix calls from the UI and Matrix event handlers.
type Container struct {
client *gomatrix.Client
gmx ifc.Gomuks
@@ -43,7 +46,8 @@ type Container struct {
typing int64
}
-func NewMatrixContainer(gmx ifc.Gomuks) *Container {
+// NewContainer creates a new Container for the given Gomuks instance.
+func NewContainer(gmx ifc.Gomuks) *Container {
c := &Container{
config: gmx.Config(),
ui: gmx.UI(),
@@ -53,6 +57,7 @@ func NewMatrixContainer(gmx ifc.Gomuks) *Container {
return c
}
+// InitClient initializes the gomatrix client and connects to the homeserver specified in the config.
func (c *Container) InitClient() error {
if len(c.config.HS) == 0 {
return fmt.Errorf("no homeserver in config")
@@ -66,7 +71,7 @@ func (c *Container) InitClient() error {
var mxid, accessToken string
if c.config.Session != nil {
accessToken = c.config.Session.AccessToken
- mxid = c.config.MXID
+ mxid = c.config.UserID
}
var err error
@@ -77,16 +82,18 @@ func (c *Container) InitClient() error {
c.stop = make(chan bool, 1)
- if c.config.Session != nil && len(c.config.Session.AccessToken) > 0 {
+ if c.config.Session != nil && len(accessToken) > 0 {
go c.Start()
}
return nil
}
+// Initialized returns whether or not the gomatrix client is initialized (see InitClient())
func (c *Container) Initialized() bool {
return c.client != nil
}
+// Login sends a password login request with the given username and password.
func (c *Container) Login(user, password string) error {
resp, err := c.client.Login(&gomatrix.ReqLogin{
Type: "m.login.password",
@@ -97,7 +104,7 @@ func (c *Container) Login(user, password string) error {
return err
}
c.client.SetCredentials(resp.UserID, resp.AccessToken)
- c.config.MXID = resp.UserID
+ c.config.UserID = resp.UserID
c.config.Save()
c.config.Session = c.config.NewSession(resp.UserID)
@@ -109,6 +116,7 @@ func (c *Container) Login(user, password string) error {
return nil
}
+// Stop stops the Matrix syncer.
func (c *Container) Stop() {
if c.running {
c.stop <- true
@@ -116,26 +124,30 @@ func (c *Container) Stop() {
}
}
+// Client returns the underlying gomatrix client object.
func (c *Container) Client() *gomatrix.Client {
return c.client
}
+// UpdatePushRules fetches the push notification rules from the server and stores them in the current Session object.
func (c *Container) UpdatePushRules() {
debug.Print("Updating push rules...")
- resp, err := gomx_ext.GetPushRules(c.client)
+ resp, err := pushrules.GetPushRules(c.client)
if err != nil {
debug.Print("Failed to fetch push rules:", err)
}
c.config.Session.PushRules = resp
}
-func (c *Container) PushRules() *gomx_ext.PushRuleset {
+// PushRules returns the push notification rules. If no push rules are cached, UpdatePushRules() will be called first.
+func (c *Container) PushRules() *pushrules.PushRuleset {
if c.config.Session.PushRules == nil {
c.UpdatePushRules()
}
return c.config.Session.PushRules
}
+// UpdateRoomList fetches the list of rooms the user has joined and sends them to the UI.
func (c *Container) UpdateRoomList() {
resp, err := c.client.JoinedRooms()
if err != nil {
@@ -151,11 +163,13 @@ func (c *Container) UpdateRoomList() {
c.ui.MainView().SetRooms(resp.JoinedRooms)
}
+// OnLogout stops the syncer and moves the UI back to the login view.
func (c *Container) OnLogout() {
c.Stop()
c.ui.SetView(ifc.ViewLogin)
}
+// OnLogin initializes the syncer and updates the room list.
func (c *Container) OnLogin() {
c.client.Store = c.config.Session
@@ -169,6 +183,7 @@ func (c *Container) OnLogin() {
c.UpdateRoomList()
}
+// Start moves the UI to the main view, calls OnLogin() and runs the syncer forever until stopped with Stop()
func (c *Container) Start() {
defer c.gmx.Recover()
@@ -197,6 +212,7 @@ func (c *Container) Start() {
}
}
+// NotifyMessage sends a desktop notification of the message with the given details.
func (c *Container) NotifyMessage(room *rooms.Room, sender, text string, critical bool) {
if room.GetTitle() != sender {
sender = fmt.Sprintf("%s (%s)", sender, room.GetTitle())
@@ -204,11 +220,12 @@ func (c *Container) NotifyMessage(room *rooms.Room, sender, text string, critica
notification.Send(sender, text, critical)
}
+// HandleMessage is the event handler for the m.room.message timeline event.
func (c *Container) HandleMessage(evt *gomatrix.Event) {
room, message := c.ui.MainView().ProcessMessageEvent(evt)
if room != nil {
pushRules := c.PushRules().GetActions(room.Room, evt).Should()
- if (pushRules.Notify || !pushRules.NotifySpecified) && evt.Sender != c.config.Session.MXID {
+ if (pushRules.Notify || !pushRules.NotifySpecified) && evt.Sender != c.config.Session.UserID {
c.NotifyMessage(room.Room, message.Sender, message.Text, pushRules.Highlight)
}
if pushRules.Highlight {
@@ -222,15 +239,17 @@ func (c *Container) HandleMessage(evt *gomatrix.Event) {
}
}
+// HandlePushRules is the event handler for the m.push_rules account data event.
func (c *Container) HandlePushRules(evt *gomatrix.Event) {
debug.Print("Received updated push rules")
var err error
- c.config.Session.PushRules, err = gomx_ext.EventToPushRules(evt)
+ c.config.Session.PushRules, err = pushrules.EventToPushRules(evt)
if err != nil {
debug.Print("Failed to convert event to push rules:", err)
}
}
+// HandleMembership is the event handler for the m.room.membership state event.
func (c *Container) HandleMembership(evt *gomatrix.Event) {
const Hour = 1 * 60 * 60 * 1000
if evt.Unsigned.Age > Hour {
@@ -249,6 +268,7 @@ func (c *Container) HandleMembership(evt *gomatrix.Event) {
}
}
+// HandleTyping is the event handler for the m.typing event.
func (c *Container) HandleTyping(evt *gomatrix.Event) {
users := evt.Content["user_ids"].([]interface{})
@@ -259,6 +279,7 @@ func (c *Container) HandleTyping(evt *gomatrix.Event) {
c.ui.MainView().SetTyping(evt.RoomID, strUsers)
}
+// SendMessage sends a message with the given text to the given room.
func (c *Container) SendMessage(roomID, text string) (string, error) {
defer c.gmx.Recover()
c.SendTyping(roomID, false)
@@ -269,6 +290,7 @@ func (c *Container) SendMessage(roomID, text string) (string, error) {
return resp.EventID, nil
}
+// SendTyping sets whether or not the user is typing in the given room.
func (c *Container) SendTyping(roomID string, typing bool) {
defer c.gmx.Recover()
ts := time.Now().Unix()
@@ -277,14 +299,15 @@ func (c *Container) SendTyping(roomID string, typing bool) {
}
if typing {
- c.client.UserTyping(roomID, true, 5000)
- c.typing = ts + 5
+ c.client.UserTyping(roomID, true, 20000)
+ c.typing = ts + 15
} else {
c.client.UserTyping(roomID, false, 0)
c.typing = 0
}
}
+// JoinRoom makes the current user try to join the given room.
func (c *Container) JoinRoom(roomID string) error {
if len(roomID) == 0 {
return fmt.Errorf("invalid room ID")
@@ -303,6 +326,7 @@ func (c *Container) JoinRoom(roomID string) error {
return nil
}
+// LeaveRoom makes the current user leave the given room.
func (c *Container) LeaveRoom(roomID string) error {
if len(roomID) == 0 {
return fmt.Errorf("invalid room ID")
@@ -316,6 +340,7 @@ func (c *Container) LeaveRoom(roomID string) error {
return nil
}
+// getState requests the state of the given room.
func (c *Container) getState(roomID string) []*gomatrix.Event {
content := make([]*gomatrix.Event, 0)
err := c.client.StateEvent(roomID, "", "", &content)
@@ -326,6 +351,7 @@ func (c *Container) getState(roomID string) []*gomatrix.Event {
return content
}
+// GetHistory fetches room history.
func (c *Container) GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.Event, string, error) {
resp, err := c.client.Messages(roomID, prevBatch, "", 'b', limit)
if err != nil {
@@ -334,6 +360,9 @@ func (c *Container) GetHistory(roomID, prevBatch string, limit int) ([]gomatrix.
return resp.Chunk, resp.End, nil
}
+// GetRoom gets the room instance stored in the session.
+//
+// If the room doesn't have any state events stored, its state will be updated.
func (c *Container) GetRoom(roomID string) *rooms.Room {
room := c.config.Session.GetRoom(roomID)
if room != nil && len(room.State) == 0 {
diff --git a/matrix/pushrules/action.go b/matrix/pushrules/action.go
new file mode 100644
index 0000000..1de973f
--- /dev/null
+++ b/matrix/pushrules/action.go
@@ -0,0 +1,135 @@
+// 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 "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)
+ } else {
+ data := string(action.Action)
+ return json.Marshal(&data)
+ }
+}
diff --git a/matrix/pushrules/condition.go b/matrix/pushrules/condition.go
new file mode 100644
index 0000000..ecbf5b2
--- /dev/null
+++ b/matrix/pushrules/condition.go
@@ -0,0 +1,147 @@
+// 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"
+
+ "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
+ }
+}
diff --git a/matrix/pushrules/doc.go b/matrix/pushrules/doc.go
new file mode 100644
index 0000000..19cd774
--- /dev/null
+++ b/matrix/pushrules/doc.go
@@ -0,0 +1,2 @@
+// Package pushrules contains utilities to parse push notification rules.
+package pushrules
diff --git a/matrix/pushrules/pushrules.go b/matrix/pushrules/pushrules.go
new file mode 100644
index 0000000..bfcb4ef
--- /dev/null
+++ b/matrix/pushrules/pushrules.go
@@ -0,0 +1,42 @@
+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) {
+ return GetScopedPushRules(client, "global")
+}
+
+// GetScopedPushRules returns the push notification rules for the given scope.
+func GetScopedPushRules(client *gomatrix.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
+}
+
+// EventToPushRules converts a m.push_rules event to a PushRuleset by passing the data through JSON.
+func EventToPushRules(event *gomatrix.Event) (*PushRuleset, error) {
+ content, _ := event.Content["global"]
+ raw, err := json.Marshal(content)
+ if err != nil {
+ return nil, err
+ }
+
+ ruleset := &PushRuleset{}
+ err = json.Unmarshal(raw, ruleset)
+ if err != nil {
+ return nil, err
+ }
+
+ return ruleset, nil
+}
+
diff --git a/matrix/pushrules/rule.go b/matrix/pushrules/rule.go
new file mode 100644
index 0000000..067bc95
--- /dev/null
+++ b/matrix/pushrules/rule.go
@@ -0,0 +1,150 @@
+// 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 (
+ "github.com/zyedidia/glob"
+ "maunium.net/go/gomatrix"
+ "maunium.net/go/gomuks/matrix/room"
+)
+
+type PushRuleArray []*PushRule
+
+func (rules PushRuleArray) setType(typ PushRuleType) PushRuleArray {
+ for _, rule := range rules {
+ rule.Type = typ
+ }
+ return rules
+}
+
+func (rules PushRuleArray) GetActions(room *rooms.Room, event *gomatrix.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 *rooms.Room, event *gomatrix.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 *rooms.Room, event *gomatrix.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 *rooms.Room, event *gomatrix.Event) bool {
+ for _, cond := range rule.Conditions {
+ if !cond.Match(room, event) {
+ return false
+ }
+ }
+ return true
+}
+
+func (rule *PushRule) matchPattern(room *rooms.Room, event *gomatrix.Event) bool {
+ pattern, err := glob.Compile(rule.Pattern)
+ if err != nil {
+ return false
+ }
+ text, _ := event.Content["body"].(string)
+ return pattern.MatchString(text)
+}
diff --git a/matrix/pushrules/ruleset.go b/matrix/pushrules/ruleset.go
new file mode 100644
index 0000000..6708b70
--- /dev/null
+++ b/matrix/pushrules/ruleset.go
@@ -0,0 +1,87 @@
+// 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 (
+ "encoding/json"
+
+ "maunium.net/go/gomatrix"
+ "maunium.net/go/gomuks/matrix/room"
+)
+
+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"`
+}
+
+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
+}
+
+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)
+}
+
+var DefaultPushActions = make(PushActionArray, 0)
+
+func (rs *PushRuleset) GetActions(room *rooms.Room, event *gomatrix.Event) (match PushActionArray) {
+ if match = rs.Override.GetActions(room, event); match != nil {
+ return
+ }
+ if match = rs.Content.GetActions(room, event); match != nil {
+ return
+ }
+ if match = rs.Room.GetActions(room, event); match != nil {
+ return
+ }
+ if match = rs.Sender.GetActions(room, event); match != nil {
+ return
+ }
+ if match = rs.Underride.GetActions(room, event); match != nil {
+ return
+ }
+ return DefaultPushActions
+}
diff --git a/matrix/room/member.go b/matrix/room/member.go
index 3b3a30c..4af20a6 100644
--- a/matrix/room/member.go
+++ b/matrix/room/member.go
@@ -20,18 +20,34 @@ import (
"maunium.net/go/gomatrix"
)
+type Membership string
+
+// The allowed membership states as specified in spec section 10.5.5.
+const (
+ MembershipJoin Membership = "join"
+ MembershipLeave Membership = "leave"
+ MembershipInvite Membership = "invite"
+ MembershipKnock Membership = "knock"
+)
+
+// Member represents a member in a room.
type Member struct {
- UserID string `json:"-"`
- Membership string `json:"membership"`
- DisplayName string `json:"displayname"`
- AvatarURL string `json:"avatar_url"`
+ // The MXID of the member.
+ UserID string `json:"-"`
+ // The membership status. Defaults to leave.
+ Membership Membership `json:"membership"`
+ // The display name of the user. Defaults to the user ID.
+ DisplayName string `json:"displayname"`
+ // The avatar URL of the user. Defaults to an empty string.
+ AvatarURL string `json:"avatar_url"`
}
+// eventToRoomMember converts a m.room.member state event into a Member object.
func eventToRoomMember(userID string, event *gomatrix.Event) *Member {
if event == nil {
return &Member{
UserID: userID,
- Membership: "leave",
+ Membership: MembershipLeave,
}
}
membership, _ := event.Content["membership"].(string)
@@ -44,7 +60,7 @@ func eventToRoomMember(userID string, event *gomatrix.Event) *Member {
return &Member{
UserID: userID,
- Membership: membership,
+ Membership: Membership(membership),
DisplayName: displayName,
AvatarURL: avatarURL,
}
diff --git a/matrix/room/room.go b/matrix/room/room.go
index 92d6c5a..264ee3b 100644
--- a/matrix/room/room.go
+++ b/matrix/room/room.go
@@ -26,11 +26,18 @@ import (
type Room struct {
*gomatrix.Room
+ // The first batch of events that has been fetched for this room.
+ // Used for fetching additional history.
PrevBatch string
- Owner string
+ // The MXID of the user whose session this room was created for.
+ SessionUserID string
+ // MXID -> Member cache calculated from membership events.
memberCache map[string]*Member
+ // The first non-SessionUserID member in the room. Calculated at the same time as memberCache.
firstMemberCache string
+ // The name of the room. Calculated from the state event name, canonical_alias or alias or the member cache.
nameCache string
+ // The topic of the room. Directly fetched from the m.room.topic state event.
topicCache string
}
@@ -135,7 +142,7 @@ func (room *Room) updateNameFromMembers() {
}
// updateNameCache updates the room display name based on the room state in the order
-// specified in section 11.2.2.5 of r0.3.0 of the Client-Server API specification.
+// specified in spec section 11.2.2.5.
func (room *Room) updateNameCache() {
if len(room.nameCache) == 0 {
room.updateNameFromNameEvent()
@@ -167,7 +174,7 @@ func (room *Room) createMemberCache() map[string]*Member {
room.firstMemberCache = ""
if events != nil {
for userID, event := range events {
- if len(room.firstMemberCache) == 0 && userID != room.Owner {
+ if len(room.firstMemberCache) == 0 && userID != room.SessionUserID {
room.firstMemberCache = userID
}
member := eventToRoomMember(userID, event)
@@ -204,7 +211,7 @@ func (room *Room) GetMember(userID string) *Member {
// NewRoom creates a new Room with the given ID
func NewRoom(roomID, owner string) *Room {
return &Room{
- Room: gomatrix.NewRoom(roomID),
- Owner: owner,
+ Room: gomatrix.NewRoom(roomID),
+ SessionUserID: owner,
}
}
diff --git a/matrix/sync.go b/matrix/sync.go
index 9eeb87a..a5f3b91 100644
--- a/matrix/sync.go
+++ b/matrix/sync.go
@@ -1,3 +1,21 @@
+// 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/>.
+
+// Based on https://github.com/matrix-org/gomatrix/blob/master/sync.go
+
package matrix
import (
@@ -26,15 +44,16 @@ func NewGomuksSyncer(session *config.Session) *GomuksSyncer {
}
}
+// ProcessResponse processes a Matrix sync response.
func (s *GomuksSyncer) ProcessResponse(res *gomatrix.RespSync, since string) (err error) {
- if !s.shouldProcessResponse(res, since) {
+ if len(since) == 0 {
return
}
// gdebug.Print("Processing sync response", since, res)
defer func() {
if r := recover(); r != nil {
- err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.Session.MXID, since, r, debug.Stack())
+ err = fmt.Errorf("ProcessResponse panicked! userID=%s since=%s panic=%s\n%s", s.Session.UserID, since, r, debug.Stack())
}
}()
@@ -99,15 +118,6 @@ func (s *GomuksSyncer) OnEventType(eventType string, callback gomatrix.OnEventLi
s.listeners[eventType] = append(s.listeners[eventType], callback)
}
-// shouldProcessResponse returns true if the response should be processed. May modify the response to remove
-// stuff that shouldn't be processed.
-func (s *GomuksSyncer) shouldProcessResponse(resp *gomatrix.RespSync, since string) bool {
- if since == "" {
- return false
- }
- return true
-}
-
func (s *GomuksSyncer) notifyListeners(event *gomatrix.Event) {
listeners, exists := s.listeners[event.Type]
if !exists {
diff --git a/ui/view-login.go b/ui/view-login.go
index c46ad25..4ae673c 100644
--- a/ui/view-login.go
+++ b/ui/view-login.go
@@ -29,7 +29,7 @@ func (ui *GomuksUI) NewLoginView() tview.Primitive {
}
homeserver := widget.NewAdvancedInputField().SetLabel("Homeserver").SetText(hs).SetFieldWidth(30)
- username := widget.NewAdvancedInputField().SetLabel("Username").SetText(ui.gmx.Config().MXID).SetFieldWidth(30)
+ username := widget.NewAdvancedInputField().SetLabel("Username").SetText(ui.gmx.Config().UserID).SetFieldWidth(30)
password := widget.NewAdvancedInputField().SetLabel("Password").SetMaskCharacter('*').SetFieldWidth(30)
ui.loginView = tview.NewForm()
diff --git a/ui/view-main.go b/ui/view-main.go
index e694d9c..8ff925c 100644
--- a/ui/view-main.go
+++ b/ui/view-main.go
@@ -418,7 +418,7 @@ func (view *MainView) processOwnMembershipChange(evt *gomatrix.Event) {
}
func (view *MainView) ProcessMembershipEvent(evt *gomatrix.Event, new bool) (room *widget.RoomView, message *types.Message) {
- if new && evt.StateKey != nil && *evt.StateKey == view.config.Session.MXID {
+ if new && evt.StateKey != nil && *evt.StateKey == view.config.Session.UserID {
view.processOwnMembershipChange(evt)
}