From b0644c3a17dfd1a55f4ed5f4f91fcc84e74359ed Mon Sep 17 00:00:00 2001 From: Alex Auvolat Date: Fri, 21 Feb 2020 19:28:00 +0100 Subject: Basic backlogging --- appservice/account.go | 12 +++++++++++- connector/connector.go | 11 +++++++++++ connector/mattermost/mattermost.go | 31 +++++++++++++++++++++++++++---- main.go | 1 + mxlib/registration.go | 1 + 5 files changed, 51 insertions(+), 5 deletions(-) diff --git a/appservice/account.go b/appservice/account.go index 3791ee3..e2f2ee9 100644 --- a/appservice/account.go +++ b/appservice/account.go @@ -230,7 +230,7 @@ func (a *Account) eventInternal(event *Event) error { err = mx.RoomInvite(mx_room_id, mx_user_id) if err != nil { if strings.Contains(err.Error(), "already in the room") { - err = nil + return nil } return err } @@ -260,6 +260,16 @@ func (a *Account) eventInternal(event *Event) error { } } + if event.Id != "" { + cache_key := fmt.Sprintf("%s/event_seen/%s/%s", + a.Protocol, mx_room_id, event.Id) + if !dbCacheTestAndSet(cache_key, "yes") { + // false: cache key was not modified, meaning we + // already saw the event + return nil + } + } + typ := "m.text" if event.Type == EVENT_ACTION { typ = "m.emote" diff --git a/connector/connector.go b/connector/connector.go index 2235318..30dfea4 100644 --- a/connector/connector.go +++ b/connector/connector.go @@ -98,6 +98,11 @@ const ( type Event struct { Type EventType + // If non-empty, the event Id is used to deduplicate events in a channel + // This is usefull for backends that provide a backlog of channel messages + // when (re-)joining a room + Id string + // UserID of the user that sent the event // If this is a direct message event, this event can only have been authored // by the user we are talking to (and not by ourself) @@ -120,12 +125,18 @@ type Event struct { type UserInfo struct { DisplayName string + + // If non-empty, the Filename of the avatar object will be used by Easybridge + // to deduplicate the update events and prevent needless reuploads. + // Example strategy that works for the mattermost backend: use the update timestamp as fictious file name Avatar MediaObject } type RoomInfo struct { Name string Topic string + + // Same deduplication comment as for UserInfo.Avatar Picture MediaObject } diff --git a/connector/mattermost/mattermost.go b/connector/mattermost/mattermost.go index 6282831..117286b 100644 --- a/connector/mattermost/mattermost.go +++ b/connector/mattermost/mattermost.go @@ -354,6 +354,22 @@ func (mm *Mattermost) handleConnected() { } else { log.Warnf("Could not get channel members: %s", resp.Error) } + + // Read backlog + // TODO: remember what was the last seen post in this channel + backlog, resp := mm.conn.Client.GetPostsForChannel(ch.Id, 0, 1000, "") + if resp.Error == nil { + for i := 0; i < len(backlog.Order); i++ { + post_id := backlog.Order[len(backlog.Order)-i-1] + post := backlog.Posts[post_id] + post_time := time.Unix(post.CreateAt/1000, 0) + post.Message = fmt.Sprintf("[%s] %s", + post_time.Format("2006-01-02 15:04:05 MST"), post.Message) + mm.handlePost(ch.Name, post, true) + } + } else { + log.Warnf("Could not get channel backlog: %s", resp.Error) + } } } @@ -425,6 +441,10 @@ func (mm *Mattermost) handlePosted(msg *model.WebSocketEvent) error { return err } + return mm.handlePost(channel_name, &post, false) +} + +func (mm *Mattermost) handlePost(channel_name string, post *model.Post, only_messages bool) error { // Skip self messages if post.UserId == mm.conn.User.Id { return nil @@ -440,6 +460,7 @@ func (mm *Mattermost) handlePosted(msg *model.WebSocketEvent) error { // Build message event msg_ev := &Event{ + Id: post.Id, Author: userId, Text: post.Message, Type: EVENT_MESSAGE, @@ -489,10 +510,12 @@ func (mm *Mattermost) handlePosted(msg *model.WebSocketEvent) error { mm.ensureJoined(user, roomId) if post.Type == "system_header_change" { - new_header := post.Props["new_header"].(string) - mm.handler.RoomInfoUpdated(roomId, userId, &RoomInfo{ - Topic: new_header, - }) + if !only_messages { + new_header := post.Props["new_header"].(string) + mm.handler.RoomInfoUpdated(roomId, userId, &RoomInfo{ + Topic: new_header, + }) + } } else if post.Type == "" || post.Type == "me" { msg_ev.Room = roomId mm.handler.Event(msg_ev) diff --git a/main.go b/main.go index 60272a7..0155892 100644 --- a/main.go +++ b/main.go @@ -102,6 +102,7 @@ func readRegistration(file string) mxlib.Registration { AsToken: hex.EncodeToString(rnd[:32]), HsToken: hex.EncodeToString(rnd[32:]), SenderLocalpart: "_ezbr_", + RateLimited: false, Namespaces: mxlib.RegistrationNamespaceSet{ Users: []mxlib.RegistrationNamespace{ mxlib.RegistrationNamespace{ diff --git a/mxlib/registration.go b/mxlib/registration.go index cae3f29..5aabdd6 100644 --- a/mxlib/registration.go +++ b/mxlib/registration.go @@ -10,6 +10,7 @@ type Registration struct { AsToken string `yaml:"as_token"` HsToken string `yaml:"hs_token"` SenderLocalpart string `yaml:"sender_localpart"` + RateLimited bool `yaml:"rate_limited"` Namespaces RegistrationNamespaceSet `yaml:"namespaces"` } -- cgit v1.2.3