From 373b25c01cc94afff3abb0fe98af31aa52a40568 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Thu, 22 Mar 2018 23:46:43 +0200 Subject: Rename matrix/room/ to matrix/rooms/ --- matrix/matrix.go | 2 +- matrix/pushrules/condition.go | 2 +- matrix/pushrules/pushrules.go | 9 +- matrix/pushrules/rule.go | 2 +- matrix/pushrules/ruleset.go | 2 +- matrix/room/doc.go | 2 - matrix/room/member.go | 67 ------------ matrix/room/room.go | 240 ------------------------------------------ matrix/rooms/doc.go | 2 + matrix/rooms/member.go | 67 ++++++++++++ matrix/rooms/room.go | 240 ++++++++++++++++++++++++++++++++++++++++++ 11 files changed, 316 insertions(+), 319 deletions(-) delete mode 100644 matrix/room/doc.go delete mode 100644 matrix/room/member.go delete mode 100644 matrix/room/room.go create mode 100644 matrix/rooms/doc.go create mode 100644 matrix/rooms/member.go create mode 100644 matrix/rooms/room.go (limited to 'matrix') diff --git a/matrix/matrix.go b/matrix/matrix.go index 19bc226..9423f63 100644 --- a/matrix/matrix.go +++ b/matrix/matrix.go @@ -26,7 +26,7 @@ import ( "maunium.net/go/gomuks/config" "maunium.net/go/gomuks/interface" "maunium.net/go/gomuks/matrix/pushrules" - "maunium.net/go/gomuks/matrix/room" + "maunium.net/go/gomuks/matrix/rooms" "maunium.net/go/gomuks/notification" "maunium.net/go/gomuks/ui/debug" "maunium.net/go/gomuks/ui/widget" diff --git a/matrix/pushrules/condition.go b/matrix/pushrules/condition.go index ecbf5b2..e9b11af 100644 --- a/matrix/pushrules/condition.go +++ b/matrix/pushrules/condition.go @@ -23,7 +23,7 @@ import ( "github.com/zyedidia/glob" "maunium.net/go/gomatrix" - "maunium.net/go/gomuks/matrix/room" + "maunium.net/go/gomuks/matrix/rooms" ) // PushCondKind is the type of a push condition. diff --git a/matrix/pushrules/pushrules.go b/matrix/pushrules/pushrules.go index bfcb4ef..876713b 100644 --- a/matrix/pushrules/pushrules.go +++ b/matrix/pushrules/pushrules.go @@ -1,12 +1,10 @@ package pushrules import ( + "encoding/json" + "net/url" -"encoding/json" -"net/url" - -"maunium.net/go/gomatrix" - + "maunium.net/go/gomatrix" ) // GetPushRules returns the push notification rules for the global scope. @@ -39,4 +37,3 @@ func EventToPushRules(event *gomatrix.Event) (*PushRuleset, error) { return ruleset, nil } - diff --git a/matrix/pushrules/rule.go b/matrix/pushrules/rule.go index 067bc95..933d493 100644 --- a/matrix/pushrules/rule.go +++ b/matrix/pushrules/rule.go @@ -19,7 +19,7 @@ package pushrules import ( "github.com/zyedidia/glob" "maunium.net/go/gomatrix" - "maunium.net/go/gomuks/matrix/room" + "maunium.net/go/gomuks/matrix/rooms" ) type PushRuleArray []*PushRule diff --git a/matrix/pushrules/ruleset.go b/matrix/pushrules/ruleset.go index 6708b70..a8d212d 100644 --- a/matrix/pushrules/ruleset.go +++ b/matrix/pushrules/ruleset.go @@ -20,7 +20,7 @@ import ( "encoding/json" "maunium.net/go/gomatrix" - "maunium.net/go/gomuks/matrix/room" + "maunium.net/go/gomuks/matrix/rooms" ) type PushRuleset struct { diff --git a/matrix/room/doc.go b/matrix/room/doc.go deleted file mode 100644 index 2a4ff4b..0000000 --- a/matrix/room/doc.go +++ /dev/null @@ -1,2 +0,0 @@ -// Package rooms contains a representation for Matrix rooms and utilities to parse state events. -package rooms diff --git a/matrix/room/member.go b/matrix/room/member.go deleted file mode 100644 index 4af20a6..0000000 --- a/matrix/room/member.go +++ /dev/null @@ -1,67 +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 . - -package rooms - -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 { - // 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: MembershipLeave, - } - } - membership, _ := event.Content["membership"].(string) - avatarURL, _ := event.Content["avatar_url"].(string) - - displayName, _ := event.Content["displayname"].(string) - if len(displayName) == 0 { - displayName = userID - } - - return &Member{ - UserID: userID, - Membership: Membership(membership), - DisplayName: displayName, - AvatarURL: avatarURL, - } -} diff --git a/matrix/room/room.go b/matrix/room/room.go deleted file mode 100644 index 4166fd7..0000000 --- a/matrix/room/room.go +++ /dev/null @@ -1,240 +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 . - -package rooms - -import ( - "fmt" - "sync" - - "maunium.net/go/gomatrix" -) - -// Room represents a single Matrix room. -type Room struct { - *gomatrix.Room - - // The first batch of events that has been fetched for this room. - // Used for fetching additional history. - PrevBatch 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 - - // fetchHistoryLock is used to make sure multiple goroutines don't fetch history for this room at the same time. - fetchHistoryLock *sync.Mutex `json:"-"` -} - -func (room *Room) LockHistory() { - if room.fetchHistoryLock == nil { - room.fetchHistoryLock = &sync.Mutex{} - } - room.fetchHistoryLock.Lock() -} - -func (room *Room) UnlockHistory() { - if room.fetchHistoryLock != nil { - room.fetchHistoryLock.Unlock() - } -} - -// UpdateState updates the room's current state with the given Event. This will clobber events based -// on the type/state_key combination. -func (room *Room) UpdateState(event *gomatrix.Event) { - _, exists := room.State[event.Type] - if !exists { - room.State[event.Type] = make(map[string]*gomatrix.Event) - } - switch event.Type { - case "m.room.member": - room.memberCache = nil - room.firstMemberCache = "" - fallthrough - case "m.room.name": - fallthrough - case "m.room.canonical_alias": - fallthrough - case "m.room.alias": - room.nameCache = "" - case "m.room.topic": - room.topicCache = "" - } - room.State[event.Type][*event.StateKey] = event -} - -// GetStateEvent returns the state event for the given type/state_key combo, or nil. -func (room *Room) GetStateEvent(eventType string, stateKey string) *gomatrix.Event { - stateEventMap, _ := room.State[eventType] - event, _ := stateEventMap[stateKey] - return event -} - -// GetStateEvents returns the state events for the given type. -func (room *Room) GetStateEvents(eventType string) map[string]*gomatrix.Event { - stateEventMap, _ := room.State[eventType] - return stateEventMap -} - -// GetTopic returns the topic of the room. -func (room *Room) GetTopic() string { - if len(room.topicCache) == 0 { - topicEvt := room.GetStateEvent("m.room.topic", "") - if topicEvt != nil { - room.topicCache, _ = topicEvt.Content["topic"].(string) - } - } - return room.topicCache -} - -// updateNameFromNameEvent updates the room display name to be the name set in the name event. -func (room *Room) updateNameFromNameEvent() { - nameEvt := room.GetStateEvent("m.room.name", "") - if nameEvt != nil { - room.nameCache, _ = nameEvt.Content["name"].(string) - } -} - -// updateNameFromCanonicalAlias updates the room display name to be the canonical alias of the room. -func (room *Room) updateNameFromCanonicalAlias() { - canonicalAliasEvt := room.GetStateEvent("m.room.canonical_alias", "") - if canonicalAliasEvt != nil { - room.nameCache, _ = canonicalAliasEvt.Content["alias"].(string) - } -} - -// updateNameFromAliases updates the room display name to be the first room alias it finds. -// -// Deprecated: the Client-Server API recommends against using aliases as display name. -func (room *Room) updateNameFromAliases() { - // TODO the spec says clients should not use m.room.aliases for room names. - // However, Riot also uses m.room.aliases, so this is here now. - aliasEvents := room.GetStateEvents("m.room.aliases") - for _, event := range aliasEvents { - aliases, _ := event.Content["aliases"].([]interface{}) - if len(aliases) > 0 { - room.nameCache, _ = aliases[0].(string) - break - } - } -} - -// updateNameFromMembers updates the room display name based on the members in this room. -// -// The room name depends on the number of users: -// Less than two users -> "Empty room" -// Exactly two users -> The display name of the other user. -// More than two users -> The display name of one of the other users, followed -// by "and X others", where X is the number of users -// excluding the local user and the named user. -func (room *Room) updateNameFromMembers() { - members := room.GetMembers() - if len(members) <= 1 { - room.nameCache = "Empty room" - } else if len(members) == 2 { - room.nameCache = members[room.firstMemberCache].DisplayName - } else { - firstMember := members[room.firstMemberCache].DisplayName - room.nameCache = fmt.Sprintf("%s and %d others", firstMember, len(members)-2) - } -} - -// updateNameCache updates the room display name based on the room state in the order -// specified in spec section 11.2.2.5. -func (room *Room) updateNameCache() { - if len(room.nameCache) == 0 { - room.updateNameFromNameEvent() - } - if len(room.nameCache) == 0 { - room.updateNameFromCanonicalAlias() - } - if len(room.nameCache) == 0 { - room.updateNameFromAliases() - } - if len(room.nameCache) == 0 { - room.updateNameFromMembers() - } -} - -// GetTitle returns the display name of the room. -// -// The display name is returned from the cache. -// If the cache is empty, it is updated first. -func (room *Room) GetTitle() string { - room.updateNameCache() - return room.nameCache -} - -// createMemberCache caches all member events into a easily processable MXID -> *Member map. -func (room *Room) createMemberCache() map[string]*Member { - cache := make(map[string]*Member) - events := room.GetStateEvents("m.room.member") - room.firstMemberCache = "" - if events != nil { - for userID, event := range events { - if len(room.firstMemberCache) == 0 && userID != room.SessionUserID { - room.firstMemberCache = userID - } - member := eventToRoomMember(userID, event) - if member.Membership != "leave" { - cache[member.UserID] = member - } - } - } - room.memberCache = cache - return cache -} - -// GetMembers returns the members in this room. -// -// The members are returned from the cache. -// If the cache is empty, it is updated first. -func (room *Room) GetMembers() map[string]*Member { - if len(room.memberCache) == 0 { - room.createMemberCache() - } - return room.memberCache -} - -// GetMember returns the member with the given MXID. -// If the member doesn't exist, nil is returned. -func (room *Room) GetMember(userID string) *Member { - if len(room.memberCache) == 0 { - room.createMemberCache() - } - member, _ := room.memberCache[userID] - return member -} - -// GetSessionOwner returns the Member instance of the user whose session this room was created for. -func (room *Room) GetSessionOwner() *Member { - return room.GetMember(room.SessionUserID) -} - -// NewRoom creates a new Room with the given ID -func NewRoom(roomID, owner string) *Room { - return &Room{ - Room: gomatrix.NewRoom(roomID), - fetchHistoryLock: &sync.Mutex{}, - SessionUserID: owner, - } -} diff --git a/matrix/rooms/doc.go b/matrix/rooms/doc.go new file mode 100644 index 0000000..2a4ff4b --- /dev/null +++ b/matrix/rooms/doc.go @@ -0,0 +1,2 @@ +// Package rooms contains a representation for Matrix rooms and utilities to parse state events. +package rooms diff --git a/matrix/rooms/member.go b/matrix/rooms/member.go new file mode 100644 index 0000000..4af20a6 --- /dev/null +++ b/matrix/rooms/member.go @@ -0,0 +1,67 @@ +// 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 rooms + +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 { + // 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: MembershipLeave, + } + } + membership, _ := event.Content["membership"].(string) + avatarURL, _ := event.Content["avatar_url"].(string) + + displayName, _ := event.Content["displayname"].(string) + if len(displayName) == 0 { + displayName = userID + } + + return &Member{ + UserID: userID, + Membership: Membership(membership), + DisplayName: displayName, + AvatarURL: avatarURL, + } +} diff --git a/matrix/rooms/room.go b/matrix/rooms/room.go new file mode 100644 index 0000000..4166fd7 --- /dev/null +++ b/matrix/rooms/room.go @@ -0,0 +1,240 @@ +// 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 rooms + +import ( + "fmt" + "sync" + + "maunium.net/go/gomatrix" +) + +// Room represents a single Matrix room. +type Room struct { + *gomatrix.Room + + // The first batch of events that has been fetched for this room. + // Used for fetching additional history. + PrevBatch 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 + + // fetchHistoryLock is used to make sure multiple goroutines don't fetch history for this room at the same time. + fetchHistoryLock *sync.Mutex `json:"-"` +} + +func (room *Room) LockHistory() { + if room.fetchHistoryLock == nil { + room.fetchHistoryLock = &sync.Mutex{} + } + room.fetchHistoryLock.Lock() +} + +func (room *Room) UnlockHistory() { + if room.fetchHistoryLock != nil { + room.fetchHistoryLock.Unlock() + } +} + +// UpdateState updates the room's current state with the given Event. This will clobber events based +// on the type/state_key combination. +func (room *Room) UpdateState(event *gomatrix.Event) { + _, exists := room.State[event.Type] + if !exists { + room.State[event.Type] = make(map[string]*gomatrix.Event) + } + switch event.Type { + case "m.room.member": + room.memberCache = nil + room.firstMemberCache = "" + fallthrough + case "m.room.name": + fallthrough + case "m.room.canonical_alias": + fallthrough + case "m.room.alias": + room.nameCache = "" + case "m.room.topic": + room.topicCache = "" + } + room.State[event.Type][*event.StateKey] = event +} + +// GetStateEvent returns the state event for the given type/state_key combo, or nil. +func (room *Room) GetStateEvent(eventType string, stateKey string) *gomatrix.Event { + stateEventMap, _ := room.State[eventType] + event, _ := stateEventMap[stateKey] + return event +} + +// GetStateEvents returns the state events for the given type. +func (room *Room) GetStateEvents(eventType string) map[string]*gomatrix.Event { + stateEventMap, _ := room.State[eventType] + return stateEventMap +} + +// GetTopic returns the topic of the room. +func (room *Room) GetTopic() string { + if len(room.topicCache) == 0 { + topicEvt := room.GetStateEvent("m.room.topic", "") + if topicEvt != nil { + room.topicCache, _ = topicEvt.Content["topic"].(string) + } + } + return room.topicCache +} + +// updateNameFromNameEvent updates the room display name to be the name set in the name event. +func (room *Room) updateNameFromNameEvent() { + nameEvt := room.GetStateEvent("m.room.name", "") + if nameEvt != nil { + room.nameCache, _ = nameEvt.Content["name"].(string) + } +} + +// updateNameFromCanonicalAlias updates the room display name to be the canonical alias of the room. +func (room *Room) updateNameFromCanonicalAlias() { + canonicalAliasEvt := room.GetStateEvent("m.room.canonical_alias", "") + if canonicalAliasEvt != nil { + room.nameCache, _ = canonicalAliasEvt.Content["alias"].(string) + } +} + +// updateNameFromAliases updates the room display name to be the first room alias it finds. +// +// Deprecated: the Client-Server API recommends against using aliases as display name. +func (room *Room) updateNameFromAliases() { + // TODO the spec says clients should not use m.room.aliases for room names. + // However, Riot also uses m.room.aliases, so this is here now. + aliasEvents := room.GetStateEvents("m.room.aliases") + for _, event := range aliasEvents { + aliases, _ := event.Content["aliases"].([]interface{}) + if len(aliases) > 0 { + room.nameCache, _ = aliases[0].(string) + break + } + } +} + +// updateNameFromMembers updates the room display name based on the members in this room. +// +// The room name depends on the number of users: +// Less than two users -> "Empty room" +// Exactly two users -> The display name of the other user. +// More than two users -> The display name of one of the other users, followed +// by "and X others", where X is the number of users +// excluding the local user and the named user. +func (room *Room) updateNameFromMembers() { + members := room.GetMembers() + if len(members) <= 1 { + room.nameCache = "Empty room" + } else if len(members) == 2 { + room.nameCache = members[room.firstMemberCache].DisplayName + } else { + firstMember := members[room.firstMemberCache].DisplayName + room.nameCache = fmt.Sprintf("%s and %d others", firstMember, len(members)-2) + } +} + +// updateNameCache updates the room display name based on the room state in the order +// specified in spec section 11.2.2.5. +func (room *Room) updateNameCache() { + if len(room.nameCache) == 0 { + room.updateNameFromNameEvent() + } + if len(room.nameCache) == 0 { + room.updateNameFromCanonicalAlias() + } + if len(room.nameCache) == 0 { + room.updateNameFromAliases() + } + if len(room.nameCache) == 0 { + room.updateNameFromMembers() + } +} + +// GetTitle returns the display name of the room. +// +// The display name is returned from the cache. +// If the cache is empty, it is updated first. +func (room *Room) GetTitle() string { + room.updateNameCache() + return room.nameCache +} + +// createMemberCache caches all member events into a easily processable MXID -> *Member map. +func (room *Room) createMemberCache() map[string]*Member { + cache := make(map[string]*Member) + events := room.GetStateEvents("m.room.member") + room.firstMemberCache = "" + if events != nil { + for userID, event := range events { + if len(room.firstMemberCache) == 0 && userID != room.SessionUserID { + room.firstMemberCache = userID + } + member := eventToRoomMember(userID, event) + if member.Membership != "leave" { + cache[member.UserID] = member + } + } + } + room.memberCache = cache + return cache +} + +// GetMembers returns the members in this room. +// +// The members are returned from the cache. +// If the cache is empty, it is updated first. +func (room *Room) GetMembers() map[string]*Member { + if len(room.memberCache) == 0 { + room.createMemberCache() + } + return room.memberCache +} + +// GetMember returns the member with the given MXID. +// If the member doesn't exist, nil is returned. +func (room *Room) GetMember(userID string) *Member { + if len(room.memberCache) == 0 { + room.createMemberCache() + } + member, _ := room.memberCache[userID] + return member +} + +// GetSessionOwner returns the Member instance of the user whose session this room was created for. +func (room *Room) GetSessionOwner() *Member { + return room.GetMember(room.SessionUserID) +} + +// NewRoom creates a new Room with the given ID +func NewRoom(roomID, owner string) *Room { + return &Room{ + Room: gomatrix.NewRoom(roomID), + fetchHistoryLock: &sync.Mutex{}, + SessionUserID: owner, + } +} -- cgit v1.2.3