forked from MirrorHub/mautrix-whatsapp
Add periodic ghost avatar resync
This commit is contained in:
parent
267799cbe0
commit
9f0901f560
8 changed files with 233 additions and 64 deletions
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
13
database/upgrades/50-puppet-background-sync.sql
Normal file
13
database/upgrades/50-puppet-background-sync.sql
Normal 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<>'';
|
47
portal.go
47
portal.go
|
@ -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
|
||||
|
|
46
puppet.go
46
puppet.go
|
@ -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
91
user.go
|
@ -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)
|
||||
|
|
Loading…
Add table
Reference in a new issue