Implement on-demand updating of room & user pictures
This commit is contained in:
parent
fd768a10be
commit
e1b838d304
4 changed files with 131 additions and 28 deletions
|
@ -137,11 +137,15 @@ func (a *Account) userInfoUpdatedInternal(user UserID, info *UserInfo) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.Avatar != nil {
|
if info.Avatar != nil {
|
||||||
|
cache_key := fmt.Sprintf("%s/user_avatar/%s", a.Protocol, user)
|
||||||
|
cache_val := info.Avatar.Filename()
|
||||||
|
if cache_val == "" || dbCacheTestAndSet(cache_key, cache_val) {
|
||||||
err2 := mx.ProfileAvatar(mx_user_id, info.Avatar)
|
err2 := mx.ProfileAvatar(mx_user_id, info.Avatar)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
err = err2
|
err = err2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -185,11 +189,15 @@ func (a *Account) roomInfoUpdatedInternal(roomId RoomID, author UserID, info *Ro
|
||||||
}
|
}
|
||||||
|
|
||||||
if info.Picture != nil {
|
if info.Picture != nil {
|
||||||
|
cache_key := fmt.Sprintf("%s/room_picture/%s", a.Protocol, roomId)
|
||||||
|
cache_val := info.Picture.Filename()
|
||||||
|
if cache_val == "" || dbCacheTestAndSet(cache_key, cache_val) {
|
||||||
err2 := mx.RoomAvatarAs(mx_room_id, info.Picture, as_mxid)
|
err2 := mx.RoomAvatarAs(mx_room_id, info.Picture, as_mxid)
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
err = err2
|
err = err2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -204,6 +212,9 @@ func (a *Account) Event(event *Event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Account) eventInternal(event *Event) error {
|
func (a *Account) eventInternal(event *Event) error {
|
||||||
|
// TODO: automatically ignore events that come from one of our bridged matrix users
|
||||||
|
// TODO: deduplicate events if we have several matrix users joined the same room (hard problem)
|
||||||
|
|
||||||
mx_user_id, err := dbGetMxUser(a.Protocol, event.Author)
|
mx_user_id, err := dbGetMxUser(a.Protocol, event.Author)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -23,6 +23,8 @@ func InitDb() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db.AutoMigrate(&DbCache{})
|
||||||
|
|
||||||
db.AutoMigrate(&DbUserMap{})
|
db.AutoMigrate(&DbUserMap{})
|
||||||
db.Model(&DbUserMap{}).AddIndex("idx_protocol_user", "protocol", "user_id")
|
db.Model(&DbUserMap{}).AddIndex("idx_protocol_user", "protocol", "user_id")
|
||||||
|
|
||||||
|
@ -35,6 +37,14 @@ func InitDb() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Long-term cache entries
|
||||||
|
type DbCache struct {
|
||||||
|
gorm.Model
|
||||||
|
|
||||||
|
Key string `gorm:"unique_index"`
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
// User mapping between protocol user IDs and puppeted matrix ids
|
// User mapping between protocol user IDs and puppeted matrix ids
|
||||||
type DbUserMap struct {
|
type DbUserMap struct {
|
||||||
gorm.Model
|
gorm.Model
|
||||||
|
@ -76,6 +86,30 @@ type DbPmRoomMap struct {
|
||||||
|
|
||||||
// ----
|
// ----
|
||||||
|
|
||||||
|
func dbCacheGet(key string) string {
|
||||||
|
var entry DbCache
|
||||||
|
if db.Where(&DbCache{Key: key}).First(&entry).RecordNotFound() {
|
||||||
|
return ""
|
||||||
|
} else {
|
||||||
|
return entry.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbCachePut(key string, value string) {
|
||||||
|
var entry DbCache
|
||||||
|
db.Where(&DbCache{Key: key}).Assign(&DbCache{Value: value}).FirstOrCreate(&entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func dbCacheTestAndSet(key string, value string) bool {
|
||||||
|
// TODO make this really an atomic operation
|
||||||
|
// True if value was changed, false if was already set
|
||||||
|
if dbCacheGet(key) != value {
|
||||||
|
dbCachePut(key, value)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func dbGetMxRoom(protocol string, roomId connector.RoomID) (string, error) {
|
func dbGetMxRoom(protocol string, roomId connector.RoomID) (string, error) {
|
||||||
var room DbRoomMap
|
var room DbRoomMap
|
||||||
|
|
||||||
|
|
|
@ -314,17 +314,21 @@ func (mm *Mattermost) handleConnected() {
|
||||||
} else {
|
} else {
|
||||||
room_info.Name = t.Team.Name + " / " + room_info.Name
|
room_info.Name = t.Team.Name + " / " + room_info.Name
|
||||||
}
|
}
|
||||||
// TODO: cache last update time so we don't do this needlessly
|
|
||||||
if t.Team.LastTeamIconUpdate > 0 {
|
if t.Team.LastTeamIconUpdate > 0 {
|
||||||
|
room_info.Picture = &LazyBlobMediaObject{
|
||||||
|
ObjectFilename: fmt.Sprintf("%s-%d",
|
||||||
|
t.Team.Name,
|
||||||
|
t.Team.LastTeamIconUpdate),
|
||||||
|
GetFn: func(o *LazyBlobMediaObject) error {
|
||||||
team_img, resp := mm.conn.Client.GetTeamIcon(t.Id, "")
|
team_img, resp := mm.conn.Client.GetTeamIcon(t.Id, "")
|
||||||
if resp.Error == nil {
|
if resp.Error == nil {
|
||||||
room_info.Picture = &BlobMediaObject{
|
|
||||||
ObjectFilename: t.Team.Name,
|
|
||||||
ObjectMimetype: http.DetectContentType(team_img),
|
|
||||||
ObjectData: team_img,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warnf("Could not get team image: %s", resp.Error)
|
log.Warnf("Could not get team image: %s", resp.Error)
|
||||||
|
return resp.Error
|
||||||
|
}
|
||||||
|
o.ObjectData = team_img
|
||||||
|
o.ObjectMimetype = http.DetectContentType(team_img)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
@ -378,22 +382,26 @@ func (mm *Mattermost) updateUserInfo(user *model.User) {
|
||||||
DisplayName: userDisp,
|
DisplayName: userDisp,
|
||||||
}
|
}
|
||||||
if user.LastPictureUpdate > 0 {
|
if user.LastPictureUpdate > 0 {
|
||||||
// TODO: cache last update time so we don't do this needlessly
|
ui.Avatar = &LazyBlobMediaObject{
|
||||||
|
ObjectFilename: fmt.Sprintf("%s-%d",
|
||||||
|
user.Username,
|
||||||
|
user.LastPictureUpdate),
|
||||||
|
GetFn: func(o *LazyBlobMediaObject) error {
|
||||||
img, resp := mm.conn.Client.GetProfileImage(user.Id, "")
|
img, resp := mm.conn.Client.GetProfileImage(user.Id, "")
|
||||||
if resp.Error == nil {
|
if resp.Error == nil {
|
||||||
ui.Avatar = &BlobMediaObject{
|
|
||||||
ObjectFilename: user.Username,
|
|
||||||
ObjectMimetype: http.DetectContentType(img),
|
|
||||||
ObjectData: img,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Warnf("Could not get profile picture: %s", resp.Error)
|
log.Warnf("Could not get profile picture: %s", resp.Error)
|
||||||
|
return resp.Error
|
||||||
|
}
|
||||||
|
o.ObjectData = img
|
||||||
|
o.ObjectMimetype = http.DetectContentType(img)
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mm.handler.UserInfoUpdated(userId, ui)
|
mm.handler.UserInfoUpdated(userId, ui)
|
||||||
mm.userdisplaynamemap[userId] = userDisp
|
mm.userdisplaynamemap[userId] = userDisp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (mm *Mattermost) ensureJoined(user *model.User, roomId RoomID) {
|
func (mm *Mattermost) ensureJoined(user *model.User, roomId RoomID) {
|
||||||
userId := UserID(fmt.Sprintf("%s@%s", user.Username, mm.server))
|
userId := UserID(fmt.Sprintf("%s@%s", user.Username, mm.server))
|
||||||
|
|
|
@ -132,3 +132,53 @@ type nullCloseReader struct {
|
||||||
func (ncr nullCloseReader) Close() error {
|
func (ncr nullCloseReader) Close() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ----
|
||||||
|
|
||||||
|
type LazyBlobMediaObject struct {
|
||||||
|
ObjectFilename string
|
||||||
|
ObjectMimetype string
|
||||||
|
ObjectImageSize *ImageSize
|
||||||
|
ObjectData []byte
|
||||||
|
|
||||||
|
GetFn func(o *LazyBlobMediaObject) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LazyBlobMediaObject) Filename() string {
|
||||||
|
return m.ObjectFilename
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LazyBlobMediaObject) Size() int64 {
|
||||||
|
if m.ObjectData == nil {
|
||||||
|
m.GetFn(m)
|
||||||
|
}
|
||||||
|
return int64(len(m.ObjectData))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LazyBlobMediaObject) Mimetype() string {
|
||||||
|
if m.ObjectData == nil {
|
||||||
|
m.GetFn(m)
|
||||||
|
}
|
||||||
|
return m.ObjectMimetype
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LazyBlobMediaObject) ImageSize() *ImageSize {
|
||||||
|
if m.ObjectData == nil {
|
||||||
|
m.GetFn(m)
|
||||||
|
}
|
||||||
|
return m.ObjectImageSize
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LazyBlobMediaObject) Read() (io.ReadCloser, error) {
|
||||||
|
if m.ObjectData == nil {
|
||||||
|
err := m.GetFn(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullCloseReader{bytes.NewBuffer(m.ObjectData)}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *LazyBlobMediaObject) URL() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue