Matrix: use per-room nicknames

Fixes #1336
This commit is contained in:
Simon THOBY
2021-07-08 19:55:08 +02:00
parent 66b6f9749d
commit 23e785deb9
2 changed files with 42 additions and 33 deletions

View File

@@ -51,17 +51,19 @@ func interface2Struct(in interface{}, out interface{}) error {
return json.Unmarshal(jsonObj, out) return json.Unmarshal(jsonObj, out)
} }
// getDisplayName retrieves the displayName for mxid, querying the homserver if the mxid is not in the cache. // getDisplayName retrieves the displayName for mxid, querying the homeserver if the mxid is not in the cache.
func (b *Bmatrix) getDisplayName(mxid string) string { func (b *Bmatrix) getDisplayName(channelID string, mxid string) string {
if b.GetBool("UseUserName") { if b.GetBool("UseUserName") {
return mxid[1:] return mxid[1:]
} }
b.RLock() b.RLock()
if val, present := b.NicknameMap[mxid]; present { if channel, channelPresent := b.NicknameMap[channelID]; channelPresent {
b.RUnlock() if val, present := channel[mxid]; present {
b.RUnlock()
return val.displayName return val.displayName
}
} }
b.RUnlock() b.RUnlock()
@@ -72,48 +74,54 @@ func (b *Bmatrix) getDisplayName(mxid string) string {
} }
if err != nil { if err != nil {
return b.cacheDisplayName(mxid, mxid[1:]) return b.cacheDisplayName(channelID, mxid, mxid[1:])
} }
return b.cacheDisplayName(mxid, displayName.DisplayName) return b.cacheDisplayName(channelID, mxid, displayName.DisplayName)
} }
// cacheDisplayName stores the mapping between a mxid and a display name, to be reused later without performing a query to the homserver. // cacheDisplayName stores the mapping between a mxid and a display name, to be reused later without performing a query to the homeserver.
// Note that old entries are cleaned when this function is called. // Note that old entries are cleaned when this function is called.
func (b *Bmatrix) cacheDisplayName(mxid string, displayName string) string { func (b *Bmatrix) cacheDisplayName(channelID string, mxid string, displayName string) string {
now := time.Now() now := time.Now()
// scan to delete old entries, to stop memory usage from becoming too high with old entries. // scan to delete old entries, to stop memory usage from becoming high with obsolete entries.
// In addition, we also detect if another user have the same username, and if so, we append their mxids to their usernames to differentiate them. // In addition, we detect if another user have the same username, and if so, we append their mxids to their usernames to differentiate them.
toDelete := []string{} toDelete := map[string]string{}
conflict := false conflict := false
b.Lock() b.Lock()
for mxid, v := range b.NicknameMap { for channelIDIter, channelEntriesIter := range b.NicknameMap {
// to prevent username reuse across matrix servers - or even on the same server, append for mxidIter, NicknameCacheIter := range channelEntriesIter {
// the mxid to the username when there is a conflict // to prevent username reuse across matrix rooms - or even inside the same room, if a user uses multiple servers -
if v.displayName == displayName { // append the mxid to the username when there is a conflict
conflict = true if NicknameCacheIter.displayName == displayName {
// TODO: it would be nice to be able to rename previous messages from this user. conflict = true
// The current behavior is that only users with clashing usernames and *that have spoken since the bridge last started* will get their mxids shown, and I don't know if that's the expected behavior. // TODO: it would be nice to be able to rename previous messages from this user.
v.displayName = fmt.Sprintf("%s (%s)", displayName, mxid) // The current behavior is that only users with clashing usernames and *that have spoken since the bridge last started* will get their mxids shown, and I don't know if that's the expected behavior.
b.NicknameMap[mxid] = v NicknameCacheIter.displayName = fmt.Sprintf("%s (%s)", displayName, mxidIter)
} b.NicknameMap[channelIDIter][mxidIter] = NicknameCacheIter
}
if now.Sub(v.lastUpdated) > 10*time.Minute { if now.Sub(NicknameCacheIter.lastUpdated) > 10*time.Minute {
toDelete = append(toDelete, mxid) toDelete[channelIDIter] = mxidIter
}
} }
} }
for channelIDIter, mxidIter := range toDelete {
delete(b.NicknameMap[channelIDIter], mxidIter)
}
if conflict { if conflict {
displayName = fmt.Sprintf("%s (%s)", displayName, mxid) displayName = fmt.Sprintf("%s (%s)", displayName, mxid)
} }
for _, v := range toDelete { if _, channelPresent := b.NicknameMap[channelID]; !channelPresent {
delete(b.NicknameMap, v) b.NicknameMap[channelID] = make(map[string]NicknameCacheEntry)
} }
b.NicknameMap[mxid] = NicknameCacheEntry{ b.NicknameMap[channelID][mxid] = NicknameCacheEntry{
displayName: displayName, displayName: displayName,
lastUpdated: now, lastUpdated: now,
} }

View File

@@ -26,9 +26,10 @@ type NicknameCacheEntry struct {
} }
type Bmatrix struct { type Bmatrix struct {
mc *matrix.Client mc *matrix.Client
UserID string UserID string
NicknameMap map[string]NicknameCacheEntry // channelId -> mxid -> NickNameCacheEntry
NicknameMap map[string]map[string]NicknameCacheEntry
RoomMap map[string]string RoomMap map[string]string
rateMutex sync.RWMutex rateMutex sync.RWMutex
sync.RWMutex sync.RWMutex
@@ -68,7 +69,7 @@ type EditedMessage struct {
func New(cfg *bridge.Config) bridge.Bridger { func New(cfg *bridge.Config) bridge.Bridger {
b := &Bmatrix{Config: cfg} b := &Bmatrix{Config: cfg}
b.RoomMap = make(map[string]string) b.RoomMap = make(map[string]string)
b.NicknameMap = make(map[string]NicknameCacheEntry) b.NicknameMap = make(map[string]map[string]NicknameCacheEntry)
return b return b
} }
@@ -342,7 +343,7 @@ func (b *Bmatrix) handleMemberChange(ev *matrix.Event) {
// Update the displayname on join messages, according to https://matrix.org/docs/spec/client_server/r0.6.1#events-on-change-of-profile-information // Update the displayname on join messages, according to https://matrix.org/docs/spec/client_server/r0.6.1#events-on-change-of-profile-information
if ev.Content["membership"] == "join" { if ev.Content["membership"] == "join" {
if dn, ok := ev.Content["displayname"].(string); ok { if dn, ok := ev.Content["displayname"].(string); ok {
b.cacheDisplayName(ev.Sender, dn) b.cacheDisplayName(ev.RoomID, ev.Sender, dn)
} }
} }
} }
@@ -360,7 +361,7 @@ func (b *Bmatrix) handleEvent(ev *matrix.Event) {
// Create our message // Create our message
rmsg := config.Message{ rmsg := config.Message{
Username: b.getDisplayName(ev.Sender), Username: b.getDisplayName(ev.RoomID, ev.Sender),
Channel: channel, Channel: channel,
Account: b.Account, Account: b.Account,
UserID: ev.Sender, UserID: ev.Sender,