Add periodic ghost avatar resync

This commit is contained in:
Tulir Asokan 2022-06-28 14:37:49 +03:00
parent 267799cbe0
commit 9f0901f560
8 changed files with 233 additions and 64 deletions

View file

@ -1069,7 +1069,7 @@ var cmdSync = &commands.FullHandler{
func fnSync(ce *WrappedCommandEvent) {
if len(ce.Args) == 0 {
ce.Reply("**Usage:** `sync <appstate/contacts/groups/space> [--create-portals]`")
ce.Reply("**Usage:** `sync <appstate/contacts/avatars/groups/space> [--contact-avatars] [--create-portals]`")
return
}
args := strings.ToLower(strings.Join(ce.Args, " "))
@ -1078,6 +1078,11 @@ func fnSync(ce *WrappedCommandEvent) {
space := strings.Contains(args, "space")
groups := strings.Contains(args, "groups") || space
createPortals := strings.Contains(args, "--create-portals")
contactAvatars := strings.Contains(args, "--contact-avatars")
if contactAvatars && (!contacts || appState) {
ce.Reply("`--contact-avatars` can only be used with `sync contacts`")
return
}
if appState {
for _, name := range appstate.AllPatchNames {
@ -1094,7 +1099,7 @@ func fnSync(ce *WrappedCommandEvent) {
}
}
} else if contacts {
err := ce.User.ResyncContacts()
err := ce.User.ResyncContacts(contactAvatars)
if err != nil {
ce.Reply("Error resyncing contacts: %v", err)
} else {

View file

@ -64,7 +64,7 @@ func (pq *PortalQuery) New() *Portal {
}
}
const portalColumns = "jid, receiver, mxid, name, topic, avatar, avatar_url, encrypted, first_event_id, next_batch_id, relay_user_id, expiration_time"
const portalColumns = "jid, receiver, mxid, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set, encrypted, first_event_id, next_batch_id, relay_user_id, expiration_time"
func (pq *PortalQuery) GetAll() []*Portal {
return pq.getAll(fmt.Sprintf("SELECT %s FROM portal", portalColumns))
@ -135,9 +135,12 @@ type Portal struct {
MXID id.RoomID
Name string
NameSet bool
Topic string
TopicSet bool
Avatar string
AvatarURL id.ContentURI
AvatarSet bool
Encrypted bool
FirstEventID id.EventID
@ -150,7 +153,7 @@ type Portal struct {
func (portal *Portal) Scan(row dbutil.Scannable) *Portal {
var mxid, avatarURL, firstEventID, nextBatchID, relayUserID sql.NullString
err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &mxid, &portal.Name, &portal.Topic, &portal.Avatar, &avatarURL, &portal.Encrypted, &firstEventID, &nextBatchID, &relayUserID, &portal.ExpirationTime)
err := row.Scan(&portal.Key.JID, &portal.Key.Receiver, &mxid, &portal.Name, &portal.NameSet, &portal.Topic, &portal.TopicSet, &portal.Avatar, &avatarURL, &portal.AvatarSet, &portal.Encrypted, &firstEventID, &nextBatchID, &relayUserID, &portal.ExpirationTime)
if err != nil {
if err != sql.ErrNoRows {
portal.log.Errorln("Database scan failed:", err)
@ -180,8 +183,14 @@ func (portal *Portal) relayUserPtr() *id.UserID {
}
func (portal *Portal) Insert() {
_, err := portal.db.Exec("INSERT INTO portal (jid, receiver, mxid, name, topic, avatar, avatar_url, encrypted, first_event_id, next_batch_id, relay_user_id, expiration_time) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)",
portal.Key.JID, portal.Key.Receiver, portal.mxidPtr(), portal.Name, portal.Topic, portal.Avatar, portal.AvatarURL.String(), portal.Encrypted, portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime)
_, err := portal.db.Exec(`
INSERT INTO portal (jid, receiver, mxid, name, name_set, topic, topic_set, avatar, avatar_url, avatar_set,
encrypted, first_event_id, next_batch_id, relay_user_id, expiration_time)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
`,
portal.Key.JID, portal.Key.Receiver, portal.mxidPtr(), portal.Name, portal.NameSet, portal.Topic, portal.TopicSet,
portal.Avatar, portal.AvatarURL.String(), portal.AvatarSet, portal.Encrypted, portal.FirstEventID.String(),
portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime)
if err != nil {
portal.log.Warnfln("Failed to insert %s: %v", portal.Key, err)
}
@ -190,11 +199,14 @@ func (portal *Portal) Insert() {
func (portal *Portal) Update(txn *sql.Tx) {
query := `
UPDATE portal
SET mxid=$1, name=$2, topic=$3, avatar=$4, avatar_url=$5, encrypted=$6, first_event_id=$7, next_batch_id=$8, relay_user_id=$9, expiration_time=$10
WHERE jid=$11 AND receiver=$12
SET mxid=$1, name=$2, name_set=$3, topic=$4, topic_set=$5, avatar=$6, avatar_url=$7, avatar_set=$8,
encrypted=$9, first_event_id=$10, next_batch_id=$11, relay_user_id=$12, expiration_time=$13
WHERE jid=$14 AND receiver=$15
`
args := []interface{}{
portal.mxidPtr(), portal.Name, portal.Topic, portal.Avatar, portal.AvatarURL.String(), portal.Encrypted, portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime, portal.Key.JID, portal.Key.Receiver,
portal.mxidPtr(), portal.Name, portal.Topic, portal.Avatar, portal.AvatarURL.String(), portal.Encrypted,
portal.FirstEventID.String(), portal.NextBatchID.String(), portal.relayUserPtr(), portal.ExpirationTime,
portal.Key.JID, portal.Key.Receiver,
}
var err error
if txn != nil {

View file

@ -18,6 +18,7 @@ package database
import (
"database/sql"
"time"
log "maunium.net/go/maulogger/v2"
@ -43,7 +44,7 @@ func (pq *PuppetQuery) New() *Puppet {
}
func (pq *PuppetQuery) GetAll() (puppets []*Puppet) {
rows, err := pq.db.Query("SELECT username, avatar, avatar_url, displayname, name_quality, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet")
rows, err := pq.db.Query("SELECT username, avatar, avatar_url, displayname, name_quality, name_set, avatar_set, last_sync, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet")
if err != nil || rows == nil {
return nil
}
@ -55,7 +56,7 @@ func (pq *PuppetQuery) GetAll() (puppets []*Puppet) {
}
func (pq *PuppetQuery) Get(jid types.JID) *Puppet {
row := pq.db.QueryRow("SELECT username, avatar, avatar_url, displayname, name_quality, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE username=$1", jid.User)
row := pq.db.QueryRow("SELECT username, avatar, avatar_url, displayname, name_quality, name_set, avatar_set, last_sync, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE username=$1", jid.User)
if row == nil {
return nil
}
@ -63,7 +64,7 @@ func (pq *PuppetQuery) Get(jid types.JID) *Puppet {
}
func (pq *PuppetQuery) GetByCustomMXID(mxid id.UserID) *Puppet {
row := pq.db.QueryRow("SELECT username, avatar, avatar_url, displayname, name_quality, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE custom_mxid=$1", mxid)
row := pq.db.QueryRow("SELECT username, avatar, avatar_url, displayname, name_quality, name_set, avatar_set, last_sync, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE custom_mxid=$1", mxid)
if row == nil {
return nil
}
@ -71,7 +72,7 @@ func (pq *PuppetQuery) GetByCustomMXID(mxid id.UserID) *Puppet {
}
func (pq *PuppetQuery) GetAllWithCustomMXID() (puppets []*Puppet) {
rows, err := pq.db.Query("SELECT username, avatar, avatar_url, displayname, name_quality, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE custom_mxid<>''")
rows, err := pq.db.Query("SELECT username, avatar, avatar_url, displayname, name_quality, name_set, avatar_set, last_sync, custom_mxid, access_token, next_batch, enable_presence, enable_receipts FROM puppet WHERE custom_mxid<>''")
if err != nil || rows == nil {
return nil
}
@ -89,8 +90,11 @@ type Puppet struct {
JID types.JID
Avatar string
AvatarURL id.ContentURI
AvatarSet bool
Displayname string
NameQuality int8
NameSet bool
LastSync time.Time
CustomMXID id.UserID
AccessToken string
@ -101,10 +105,10 @@ type Puppet struct {
func (puppet *Puppet) Scan(row dbutil.Scannable) *Puppet {
var displayname, avatar, avatarURL, customMXID, accessToken, nextBatch sql.NullString
var quality sql.NullInt64
var enablePresence, enableReceipts sql.NullBool
var quality, lastSync sql.NullInt64
var enablePresence, enableReceipts, nameSet, avatarSet sql.NullBool
var username string
err := row.Scan(&username, &avatar, &avatarURL, &displayname, &quality, &customMXID, &accessToken, &nextBatch, &enablePresence, &enableReceipts)
err := row.Scan(&username, &avatar, &avatarURL, &displayname, &quality, &nameSet, &avatarSet, &lastSync, &customMXID, &accessToken, &nextBatch, &enablePresence, &enableReceipts)
if err != nil {
if err != sql.ErrNoRows {
puppet.log.Errorln("Database scan failed:", err)
@ -116,6 +120,11 @@ func (puppet *Puppet) Scan(row dbutil.Scannable) *Puppet {
puppet.Avatar = avatar.String
puppet.AvatarURL, _ = id.ParseContentURI(avatarURL.String)
puppet.NameQuality = int8(quality.Int64)
puppet.NameSet = nameSet.Bool
puppet.AvatarSet = avatarSet.Bool
if lastSync.Int64 > 0 {
puppet.LastSync = time.Unix(lastSync.Int64, 0)
}
puppet.CustomMXID = id.UserID(customMXID.String)
puppet.AccessToken = accessToken.String
puppet.NextBatch = nextBatch.String
@ -129,16 +138,36 @@ func (puppet *Puppet) Insert() {
puppet.log.Warnfln("Not inserting %s: not a user", puppet.JID)
return
}
_, err := puppet.db.Exec("INSERT INTO puppet (username, avatar, avatar_url, displayname, name_quality, custom_mxid, access_token, next_batch, enable_presence, enable_receipts) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
puppet.JID.User, puppet.Avatar, puppet.AvatarURL.String(), puppet.Displayname, puppet.NameQuality, puppet.CustomMXID, puppet.AccessToken, puppet.NextBatch, puppet.EnablePresence, puppet.EnableReceipts)
var lastSyncTs int64
if !puppet.LastSync.IsZero() {
lastSyncTs = puppet.LastSync.Unix()
}
_, err := puppet.db.Exec(`
INSERT INTO puppet (username, avatar, avatar_url, avatar_set, displayname, name_quality, name_set, last_sync,
custom_mxid, access_token, next_batch, enable_presence, enable_receipts)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)
`, puppet.JID.User, puppet.Avatar, puppet.AvatarURL.String(), puppet.AvatarSet, puppet.Displayname,
puppet.NameQuality, puppet.NameSet, lastSyncTs, puppet.CustomMXID, puppet.AccessToken, puppet.NextBatch,
puppet.EnablePresence, puppet.EnableReceipts,
)
if err != nil {
puppet.log.Warnfln("Failed to insert %s: %v", puppet.JID, err)
}
}
func (puppet *Puppet) Update() {
_, err := puppet.db.Exec("UPDATE puppet SET displayname=$1, name_quality=$2, avatar=$3, avatar_url=$4, custom_mxid=$5, access_token=$6, next_batch=$7, enable_presence=$8, enable_receipts=$9 WHERE username=$10",
puppet.Displayname, puppet.NameQuality, puppet.Avatar, puppet.AvatarURL.String(), puppet.CustomMXID, puppet.AccessToken, puppet.NextBatch, puppet.EnablePresence, puppet.EnableReceipts, puppet.JID.User)
var lastSyncTs int64
if !puppet.LastSync.IsZero() {
lastSyncTs = puppet.LastSync.Unix()
}
_, err := puppet.db.Exec(`
UPDATE puppet
SET displayname=$1, name_quality=$2, name_set=$3, avatar=$4, avatar_url=$5, avatar_set=$6, last_sync=$7,
custom_mxid=$8, access_token=$9, next_batch=$10, enable_presence=$11, enable_receipts=$12
WHERE username=$13
`, puppet.Displayname, puppet.NameQuality, puppet.NameSet, puppet.Avatar, puppet.AvatarURL.String(), puppet.AvatarSet,
lastSyncTs, puppet.CustomMXID, puppet.AccessToken, puppet.NextBatch, puppet.EnablePresence, puppet.EnableReceipts,
puppet.JID.User)
if err != nil {
puppet.log.Warnfln("Failed to update %s: %v", puppet.JID, err)
}

View file

@ -1,4 +1,4 @@
-- v0 -> v49: Latest revision
-- v0 -> v50: Latest revision
CREATE TABLE "user" (
mxid TEXT PRIMARY KEY,
@ -19,10 +19,13 @@ CREATE TABLE portal (
jid TEXT,
receiver TEXT,
mxid TEXT UNIQUE,
name TEXT NOT NULL,
topic TEXT NOT NULL,
avatar TEXT NOT NULL,
name TEXT NOT NULL,
name_set BOOLEAN NOT NULL DEFAULT false,
topic TEXT NOT NULL,
topic_set BOOLEAN NOT NULL DEFAULT false,
avatar TEXT NOT NULL,
avatar_url TEXT,
avatar_set BOOLEAN NOT NULL DEFAULT false,
encrypted BOOLEAN NOT NULL DEFAULT false,
first_event_id TEXT,
@ -39,6 +42,9 @@ CREATE TABLE puppet (
name_quality SMALLINT,
avatar TEXT,
avatar_url TEXT,
name_set BOOLEAN NOT NULL DEFAULT false,
avatar_set BOOLEAN NOT NULL DEFAULT false,
last_sync BIGINT NOT NULL DEFAULT 0,
custom_mxid TEXT,
access_token TEXT,

View file

@ -0,0 +1,13 @@
-- v50: Add last sync timestamp for puppets
ALTER TABLE puppet ADD COLUMN last_sync BIGINT NOT NULL DEFAULT 0;
ALTER TABLE puppet ADD COLUMN name_set BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE puppet ADD COLUMN avatar_set BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE portal ADD COLUMN name_set BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE portal ADD COLUMN avatar_set BOOLEAN NOT NULL DEFAULT false;
ALTER TABLE portal ADD COLUMN topic_set BOOLEAN NOT NULL DEFAULT false;
UPDATE puppet SET name_set=true WHERE displayname<>'';
UPDATE puppet SET avatar_set=true WHERE avatar<>'';
UPDATE portal SET name_set=true WHERE name<>'';
UPDATE portal SET avatar_set=true WHERE avatar<>'';
UPDATE portal SET topic_set=true WHERE topic<>'';

View file

@ -811,20 +811,20 @@ func (portal *Portal) markHandled(txn *sql.Tx, msg *database.Message, info *type
return msg
}
func (portal *Portal) getMessagePuppet(user *User, info *types.MessageInfo) *Puppet {
func (portal *Portal) getMessagePuppet(user *User, info *types.MessageInfo) (puppet *Puppet) {
if info.IsFromMe {
return portal.bridge.GetPuppetByJID(user.JID)
} else if portal.IsPrivateChat() {
return portal.bridge.GetPuppetByJID(portal.Key.JID)
puppet = portal.bridge.GetPuppetByJID(portal.Key.JID)
} else {
puppet := portal.bridge.GetPuppetByJID(info.Sender)
if puppet == nil {
portal.log.Warnfln("Message %+v doesn't seem to have a valid sender (%s): puppet is nil", *info, info.Sender)
return nil
}
puppet.SyncContact(user, true, true, "handling message")
return puppet
puppet = portal.bridge.GetPuppetByJID(info.Sender)
}
if puppet == nil {
portal.log.Warnfln("Message %+v doesn't seem to have a valid sender (%s): puppet is nil", *info, info.Sender)
return nil
}
puppet.SyncContact(user, true, true, "handling message")
return puppet
}
func (portal *Portal) getMessageIntent(user *User, info *types.MessageInfo) *appservice.IntentAPI {
@ -939,12 +939,12 @@ func (portal *Portal) UpdateAvatar(user *User, setBy types.JID, updateInfo bool)
}
return false
} else if avatar == nil {
if portal.Avatar == "remove" {
if portal.Avatar == "remove" && portal.AvatarSet {
return false
}
portal.AvatarURL = id.ContentURI{}
avatar = &types.ProfilePictureInfo{ID: "remove"}
} else if avatar.ID == portal.Avatar {
} else if avatar.ID == portal.Avatar && portal.AvatarSet {
return false
} else if len(avatar.URL) == 0 {
portal.log.Warnln("Didn't get URL in response to avatar query")
@ -957,6 +957,8 @@ func (portal *Portal) UpdateAvatar(user *User, setBy types.JID, updateInfo bool)
}
portal.AvatarURL = url
}
portal.Avatar = avatar.ID
portal.AvatarSet = false
if len(portal.MXID) > 0 {
intent := portal.MainIntent()
@ -970,11 +972,13 @@ func (portal *Portal) UpdateAvatar(user *User, setBy types.JID, updateInfo bool)
if err != nil {
portal.log.Warnln("Failed to set room avatar:", err)
return false
} else {
portal.AvatarSet = true
}
}
portal.Avatar = avatar.ID
if updateInfo {
portal.UpdateBridgeInfo()
portal.Update(nil)
}
return true
}
@ -983,9 +987,10 @@ func (portal *Portal) UpdateName(name string, setBy types.JID, updateInfo bool)
if name == "" && portal.IsBroadcastList() {
name = UnnamedBroadcastName
}
if portal.Name != name {
portal.log.Debugfln("Updating name %s -> %s", portal.Name, name)
if portal.Name != name || !portal.NameSet {
portal.log.Debugfln("Updating name %q -> %q", portal.Name, name)
portal.Name = name
portal.NameSet = false
intent := portal.MainIntent()
if !setBy.IsEmpty() {
@ -996,12 +1001,13 @@ func (portal *Portal) UpdateName(name string, setBy types.JID, updateInfo bool)
_, err = portal.MainIntent().SetRoomName(portal.MXID, name)
}
if err == nil {
portal.NameSet = true
if updateInfo {
portal.UpdateBridgeInfo()
portal.Update(nil)
}
return true
} else {
portal.Name = ""
portal.log.Warnln("Failed to set room name:", err)
}
}
@ -1009,9 +1015,10 @@ func (portal *Portal) UpdateName(name string, setBy types.JID, updateInfo bool)
}
func (portal *Portal) UpdateTopic(topic string, setBy types.JID, updateInfo bool) bool {
if portal.Topic != topic {
portal.log.Debugfln("Updating topic %s -> %s", portal.Topic, topic)
if portal.Topic != topic || !portal.TopicSet {
portal.log.Debugfln("Updating topic %q -> %q", portal.Topic, topic)
portal.Topic = topic
portal.TopicSet = false
intent := portal.MainIntent()
if !setBy.IsEmpty() {
@ -1022,12 +1029,13 @@ func (portal *Portal) UpdateTopic(topic string, setBy types.JID, updateInfo bool
_, err = portal.MainIntent().SetRoomTopic(portal.MXID, topic)
}
if err == nil {
portal.TopicSet = true
if updateInfo {
portal.UpdateBridgeInfo()
portal.Update(nil)
}
return true
} else {
portal.Topic = ""
portal.log.Warnln("Failed to set room topic:", err)
}
}
@ -1376,6 +1384,7 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
Parsed: event.RoomAvatarEventContent{URL: portal.AvatarURL},
},
})
portal.AvatarSet = true
}
var invite []id.UserID
@ -1410,6 +1419,8 @@ func (portal *Portal) CreateMatrixRoom(user *User, groupInfo *types.GroupInfo, i
if err != nil {
return err
}
portal.NameSet = len(portal.Name) > 0
portal.TopicSet = len(portal.Topic) > 0
portal.MXID = resp.RoomID
portal.bridge.portalsLock.Lock()
portal.bridge.portalsByMXID[portal.MXID] = portal

View file

@ -244,21 +244,22 @@ func (puppet *Puppet) UpdateAvatar(source *User) bool {
puppet.log.Warnln("Failed to get avatar URL:", err)
} else if puppet.Avatar == "" {
puppet.Avatar = "unauthorized"
puppet.AvatarSet = false
return true
}
return false
} else if avatar == nil {
if puppet.Avatar == "remove" {
if puppet.Avatar == "remove" && puppet.AvatarSet {
return false
}
puppet.AvatarURL = id.ContentURI{}
avatar = &types.ProfilePictureInfo{ID: "remove"}
} else if avatar.ID == puppet.Avatar {
} else if avatar.ID == puppet.Avatar && puppet.AvatarSet {
return false
} else if len(avatar.URL) == 0 {
puppet.log.Warnln("Didn't get URL in response to avatar query")
return false
} else {
} else if avatar.ID != puppet.Avatar || puppet.AvatarURL.IsEmpty() {
url, err := reuploadAvatar(puppet.DefaultIntent(), avatar.URL)
if err != nil {
puppet.log.Warnln("Failed to reupload avatar:", err)
@ -267,27 +268,31 @@ func (puppet *Puppet) UpdateAvatar(source *User) bool {
puppet.AvatarURL = url
}
puppet.Avatar = avatar.ID
puppet.AvatarSet = false
err = puppet.DefaultIntent().SetAvatarURL(puppet.AvatarURL)
if err != nil {
puppet.log.Warnln("Failed to set avatar:", err)
} else {
puppet.log.Debugln("Updated avatar", puppet.Avatar, "->", avatar.ID)
puppet.AvatarSet = true
}
puppet.log.Debugln("Updated avatar", puppet.Avatar, "->", avatar.ID)
puppet.Avatar = avatar.ID
go puppet.updatePortalAvatar()
return true
}
func (puppet *Puppet) UpdateName(source *User, contact types.ContactInfo) bool {
func (puppet *Puppet) UpdateName(contact types.ContactInfo) bool {
newName, quality := puppet.bridge.Config.Bridge.FormatDisplayname(puppet.JID, contact)
if puppet.Displayname != newName && quality >= puppet.NameQuality {
if (puppet.Displayname != newName || !puppet.NameSet) && quality >= puppet.NameQuality {
puppet.Displayname = newName
puppet.NameQuality = quality
puppet.NameSet = false
err := puppet.DefaultIntent().SetDisplayName(newName)
if err == nil {
puppet.log.Debugln("Updated name", puppet.Displayname, "->", newName)
puppet.Displayname = newName
puppet.NameQuality = quality
puppet.NameSet = true
go puppet.updatePortalName()
puppet.Update()
} else {
puppet.log.Warnln("Failed to set display name:", err)
}
@ -336,6 +341,7 @@ func (puppet *Puppet) updatePortalName() {
func (puppet *Puppet) SyncContact(source *User, onlyIfNoName, shouldHavePushName bool, reason string) {
if onlyIfNoName && len(puppet.Displayname) > 0 && (!shouldHavePushName || puppet.NameQuality > config.NameQualityPhone) {
source.EnqueuePuppetResync(puppet)
return
}
@ -345,10 +351,10 @@ func (puppet *Puppet) SyncContact(source *User, onlyIfNoName, shouldHavePushName
} else if !contact.Found {
puppet.log.Warnfln("No contact info found through %s in SyncContact (sync reason: %s)", source.MXID, reason)
}
puppet.Sync(source, contact)
puppet.Sync(source, &contact, false)
}
func (puppet *Puppet) Sync(source *User, contact types.ContactInfo) {
func (puppet *Puppet) Sync(source *User, contact *types.ContactInfo, forceAvatarSync bool) {
puppet.syncLock.Lock()
defer puppet.syncLock.Unlock()
err := puppet.DefaultIntent().EnsureRegistered()
@ -356,16 +362,20 @@ func (puppet *Puppet) Sync(source *User, contact types.ContactInfo) {
puppet.log.Errorln("Failed to ensure registered:", err)
}
if puppet.JID.User == source.JID.User {
contact.PushName = source.Client.Store.PushName
}
puppet.log.Debugfln("Syncing info through %s", source.JID)
update := false
update = puppet.UpdateName(source, contact) || update
if len(puppet.Avatar) == 0 || puppet.bridge.Config.Bridge.UserAvatarSync {
if contact != nil {
if puppet.JID.User == source.JID.User {
contact.PushName = source.Client.Store.PushName
}
update = puppet.UpdateName(*contact) || update
}
if len(puppet.Avatar) == 0 || forceAvatarSync || puppet.bridge.Config.Bridge.UserAvatarSync {
update = puppet.UpdateAvatar(source) || update
}
if update {
if update || puppet.LastSync.Add(24*time.Hour).Before(time.Now()) {
puppet.LastSync = time.Now()
puppet.Update()
}
}

91
user.go
View file

@ -83,6 +83,11 @@ type User struct {
BackfillQueue *BackfillQueue
BridgeState *bridge.BridgeStateQueue
puppetResyncQueue []*Puppet
puppetResyncQueueDedup map[types.JID]struct{}
puppetResyncQueueLock sync.Mutex
nextPuppetResync time.Time
}
func (br *WABridge) getUserByMXID(userID id.UserID, onlyIfExists bool) *User {
@ -216,6 +221,8 @@ func (br *WABridge) NewUser(dbUser *database.User) *User {
historySyncs: make(chan *events.HistorySync, 32),
lastPresence: types.PresenceUnavailable,
puppetResyncQueueDedup: make(map[types.JID]struct{}),
}
user.PermissionLevel = user.bridge.Config.Bridge.Permissions.Get(user.MXID)
@ -223,9 +230,85 @@ func (br *WABridge) NewUser(dbUser *database.User) *User {
user.Whitelisted = user.PermissionLevel >= bridgeconfig.PermissionLevelUser
user.Admin = user.PermissionLevel >= bridgeconfig.PermissionLevelAdmin
user.BridgeState = br.NewBridgeStateQueue(user, user.log)
go user.puppetResyncLoop()
return user
}
const puppetSyncMinInterval = 7 * 24 * time.Hour
const puppetSyncLoopInterval = 4 * time.Hour
func (user *User) puppetResyncLoop() {
user.nextPuppetResync = time.Now().Add(puppetSyncLoopInterval)
for {
time.Sleep(user.nextPuppetResync.Sub(time.Now()))
user.nextPuppetResync = time.Now().Add(puppetSyncLoopInterval)
user.doPuppetResync()
}
}
func (user *User) EnqueuePuppetResync(puppet *Puppet) {
if puppet.LastSync.Add(puppetSyncMinInterval).After(time.Now()) {
return
}
user.puppetResyncQueueLock.Lock()
if _, exists := user.puppetResyncQueueDedup[puppet.JID]; !exists {
user.puppetResyncQueueDedup[puppet.JID] = struct{}{}
user.puppetResyncQueue = append(user.puppetResyncQueue, puppet)
user.log.Infofln("Enqueued resync for %s (next sync in %s)", puppet.JID, user.nextPuppetResync.Sub(time.Now()))
}
user.puppetResyncQueueLock.Unlock()
}
func (user *User) doPuppetResync() {
if !user.IsLoggedIn() {
return
}
user.puppetResyncQueueLock.Lock()
if len(user.puppetResyncQueue) == 0 {
user.puppetResyncQueueLock.Unlock()
return
}
queue := user.puppetResyncQueue
user.puppetResyncQueue = nil
user.puppetResyncQueueDedup = make(map[types.JID]struct{})
user.puppetResyncQueueLock.Unlock()
var jids []types.JID
var filteredPuppets []*Puppet
for _, puppet := range queue {
if puppet.LastSync.Add(puppetSyncMinInterval).After(time.Now()) {
user.log.Debugfln("Not resyncing %s, last sync was %s ago", puppet.JID, time.Now().Sub(puppet.LastSync))
continue
}
jids = append(jids, puppet.JID)
filteredPuppets = append(filteredPuppets, puppet)
}
if len(jids) == 0 {
user.log.Debugfln("Skipping background sync, all puppets in queue have been synced in the past 3 days")
return
}
user.log.Debugfln("Doing background sync for %+v", jids)
infos, err := user.Client.GetUserInfo(jids)
if err != nil {
user.log.Errorfln("Error getting user info for background sync: %v", err)
return
}
for _, puppet := range filteredPuppets {
info, ok := infos[puppet.JID]
if !ok {
user.log.Warnfln("Didn't get info for %s in background sync", puppet.JID)
continue
}
var contactPtr *types.ContactInfo
contact, err := user.Session.Contacts.GetContact(puppet.JID)
if err != nil {
user.log.Warnfln("Failed to get contact info for %s in background sync: %v", puppet.JID, err)
} else if contact.Found {
contactPtr = &contact
}
puppet.Sync(user, contactPtr, info.PictureID != puppet.Avatar)
}
}
func (user *User) ensureInvited(intent *appservice.IntentAPI, roomID id.RoomID, isDirect bool) (ok bool) {
inviteContent := event.Content{
Parsed: &event.MemberEventContent{
@ -670,7 +753,7 @@ func (user *User) HandleEvent(event interface{}) {
}
} else if v.Name == appstate.WAPatchCriticalUnblockLow {
go func() {
err := user.ResyncContacts()
err := user.ResyncContacts(false)
if err != nil {
user.log.Errorln("Failed to resync puppets: %v", err)
}
@ -1020,7 +1103,7 @@ func (user *User) syncPuppet(jid types.JID, reason string) {
user.bridge.GetPuppetByJID(jid).SyncContact(user, false, false, reason)
}
func (user *User) ResyncContacts() error {
func (user *User) ResyncContacts(forceAvatarSync bool) error {
contacts, err := user.Client.Store.Contacts.GetAllContacts()
if err != nil {
return fmt.Errorf("failed to get cached contacts: %w", err)
@ -1029,7 +1112,7 @@ func (user *User) ResyncContacts() error {
for jid, contact := range contacts {
puppet := user.bridge.GetPuppetByJID(jid)
if puppet != nil {
puppet.Sync(user, contact)
puppet.Sync(user, &contact, forceAvatarSync)
} else {
user.log.Warnfln("Got a nil puppet for %s while syncing contacts", jid)
}
@ -1196,7 +1279,7 @@ func (user *User) handlePictureUpdate(evt *events.Picture) {
puppet := user.bridge.GetPuppetByJID(evt.JID)
user.log.Debugfln("Received picture update for puppet %s (current: %s, new: %s)", evt.JID, puppet.Avatar, evt.PictureID)
if puppet.Avatar != evt.PictureID {
puppet.UpdateAvatar(user)
puppet.Sync(user, nil, true)
}
} else if portal := user.GetPortalByJID(evt.JID); portal != nil {
user.log.Debugfln("Received picture update for portal %s (current: %s, new: %s)", evt.JID, portal.Avatar, evt.PictureID)