// mautrix-groupme - A Matrix-GroupMe puppeting bridge. // Copyright (C) 2022 Sumner Evans, Karmanyaah Malhotra // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package main import ( "regexp" "sync" log "maunium.net/go/maulogger/v2" "gitea.watsonlabs.net/watsonb8/groupme-lib" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/id" "github.com/beeper/groupme/database" ) var userIDRegex *regexp.Regexp type Puppet struct { *database.Puppet bridge *GMBridge log log.Logger typingIn id.RoomID typingAt int64 MXID id.UserID customIntent *appservice.IntentAPI customTypingIn map[id.RoomID]bool customUser *User syncLock sync.Mutex } func (puppet *Puppet) ClearCustomMXID() { //TODO implement me panic("implement me") } // Public Properties func (puppet *Puppet) GetMXID() id.UserID { return puppet.MXID } func (puppet *Puppet) PhoneNumber() string { return puppet.GMID.String() } // Public Methods func (puppet *Puppet) IntentFor(portal *Portal) *appservice.IntentAPI { if puppet.customIntent == nil || portal.Key.GMID == puppet.GMID { return puppet.DefaultIntent() } return puppet.customIntent } func (puppet *Puppet) CustomIntent() *appservice.IntentAPI { return puppet.customIntent } func (puppet *Puppet) DefaultIntent() *appservice.IntentAPI { return puppet.bridge.AS.Intent(puppet.MXID) } func (puppet *Puppet) UpdateAvatar(source *User, forcePortalSync bool) bool { changed := source.updateAvatar(puppet.GMID, &puppet.Avatar, &puppet.AvatarURL, &puppet.AvatarSet, puppet.log, puppet.DefaultIntent()) if !changed || puppet.Avatar == "unauthorized" { if forcePortalSync { go puppet.updatePortalAvatar() } return changed } err := puppet.DefaultIntent().SetAvatarURL(puppet.AvatarURL) if err != nil { puppet.log.Warnln("Failed to set avatar:", err) } else { puppet.AvatarSet = true } go puppet.updatePortalAvatar() return true } func (puppet *Puppet) UpdateName(member groupme.Member, forcePortalSync bool) bool { newName := puppet.bridge.Config.Bridge.FormatDisplayname(puppet.GMID, member) if puppet.Displayname != newName || !puppet.NameSet { oldName := puppet.Displayname puppet.Displayname = newName puppet.NameSet = false err := puppet.DefaultIntent().SetDisplayName(newName) if err == nil { puppet.log.Debugln("Updated name", oldName, "->", newName) puppet.NameSet = true go puppet.updatePortalName() } else { puppet.log.Warnln("Failed to set display name:", err) } return true } else if forcePortalSync { go puppet.updatePortalName() } return false } func (puppet *Puppet) updatePortalMeta(meta func(portal *Portal)) { if puppet.bridge.Config.Bridge.PrivateChatPortalMeta || puppet.bridge.Config.Bridge.Encryption.Allow { for _, portal := range puppet.bridge.GetAllPortalsByGMID(puppet.GMID) { if !puppet.bridge.Config.Bridge.PrivateChatPortalMeta && !portal.Encrypted { continue } // Get room create lock to prevent races between receiving contact info and room creation. portal.roomCreateLock.Lock() meta(portal) portal.roomCreateLock.Unlock() } } } func (puppet *Puppet) updatePortalAvatar() { puppet.updatePortalMeta(func(portal *Portal) { if portal.Avatar == puppet.Avatar && portal.AvatarURL == puppet.AvatarURL && portal.AvatarSet { return } portal.AvatarURL = puppet.AvatarURL portal.Avatar = puppet.Avatar portal.AvatarSet = false defer portal.Update(nil) if len(portal.MXID) > 0 { _, err := portal.MainIntent().SetRoomAvatar(portal.MXID, puppet.AvatarURL) if err != nil { portal.log.Warnln("Failed to set avatar:", err) } else { portal.AvatarSet = true portal.UpdateBridgeInfo() } } }) } func (puppet *Puppet) updatePortalName() { puppet.updatePortalMeta(func(portal *Portal) { portal.UpdateName(puppet.Displayname, groupme.ID(""), true) }) } func (puppet *Puppet) Sync(source *User, member *groupme.Member, forceAvatarSync bool, forcePortalSync bool) { puppet.syncLock.Lock() defer puppet.syncLock.Unlock() err := puppet.DefaultIntent().EnsureRegistered() if err != nil { puppet.log.Errorln("Failed to ensure registered:", err) } update := false update = puppet.UpdateName(*member, forcePortalSync) || update update = puppet.UpdateAvatar(source, forcePortalSync) || update if update { puppet.Update() } }