aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Auvolat <alex@adnab.me>2020-02-21 18:43:47 +0100
committerAlex Auvolat <alex@adnab.me>2020-02-21 18:43:47 +0100
commite1b838d30493effbcd8a23fe43e2b131c745b722 (patch)
tree6f5cab8d0e91c8c248cb2e79c1d116de99273c83
parentfd768a10be36ec31f674fa291fcbe77b78a2855c (diff)
downloadeasybridge-e1b838d30493effbcd8a23fe43e2b131c745b722.tar.gz
easybridge-e1b838d30493effbcd8a23fe43e2b131c745b722.zip
Implement on-demand updating of room & user pictures
-rw-r--r--appservice/account.go23
-rw-r--r--appservice/db.go34
-rw-r--r--connector/mattermost/mattermost.go52
-rw-r--r--connector/mediaobject.go50
4 files changed, 131 insertions, 28 deletions
diff --git a/appservice/account.go b/appservice/account.go
index 2df2930..3791ee3 100644
--- a/appservice/account.go
+++ b/appservice/account.go
@@ -137,9 +137,13 @@ func (a *Account) userInfoUpdatedInternal(user UserID, info *UserInfo) error {
}
if info.Avatar != nil {
- err2 := mx.ProfileAvatar(mx_user_id, info.Avatar)
- if err2 != nil {
- err = err2
+ 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)
+ if err2 != nil {
+ err = err2
+ }
}
}
@@ -185,9 +189,13 @@ func (a *Account) roomInfoUpdatedInternal(roomId RoomID, author UserID, info *Ro
}
if info.Picture != nil {
- err2 := mx.RoomAvatarAs(mx_room_id, info.Picture, as_mxid)
- if err2 != nil {
- err = err2
+ 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)
+ if err2 != nil {
+ err = err2
+ }
}
}
@@ -204,6 +212,9 @@ func (a *Account) Event(event *Event) {
}
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)
if err != nil {
return err
diff --git a/appservice/db.go b/appservice/db.go
index 5cefd30..646dae3 100644
--- a/appservice/db.go
+++ b/appservice/db.go
@@ -23,6 +23,8 @@ func InitDb() error {
return err
}
+ db.AutoMigrate(&DbCache{})
+
db.AutoMigrate(&DbUserMap{})
db.Model(&DbUserMap{}).AddIndex("idx_protocol_user", "protocol", "user_id")
@@ -35,6 +37,14 @@ func InitDb() error {
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
type DbUserMap struct {
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) {
var room DbRoomMap
diff --git a/connector/mattermost/mattermost.go b/connector/mattermost/mattermost.go
index 73ea66b..6282831 100644
--- a/connector/mattermost/mattermost.go
+++ b/connector/mattermost/mattermost.go
@@ -314,17 +314,21 @@ func (mm *Mattermost) handleConnected() {
} else {
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 {
- team_img, resp := mm.conn.Client.GetTeamIcon(t.Id, "")
- 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)
+ 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, "")
+ if resp.Error == nil {
+ 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
@@ -378,20 +382,24 @@ func (mm *Mattermost) updateUserInfo(user *model.User) {
DisplayName: userDisp,
}
if user.LastPictureUpdate > 0 {
- // TODO: cache last update time so we don't do this needlessly
- img, resp := mm.conn.Client.GetProfileImage(user.Id, "")
- 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)
+ 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, "")
+ if resp.Error == nil {
+ 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.userdisplaynamemap[userId] = userDisp
}
+ mm.handler.UserInfoUpdated(userId, ui)
+ mm.userdisplaynamemap[userId] = userDisp
}
}
diff --git a/connector/mediaobject.go b/connector/mediaobject.go
index a8d6f9a..244e571 100644
--- a/connector/mediaobject.go
+++ b/connector/mediaobject.go
@@ -132,3 +132,53 @@ type nullCloseReader struct {
func (ncr nullCloseReader) Close() error {
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 ""
+}