From 38f89c4df2f473224bba13495a698ba2c194762b Mon Sep 17 00:00:00 2001 From: Haw Loeung Date: Thu, 14 Sep 2023 06:07:50 +1000 Subject: [PATCH] Add support for searching scrollback for specific posts/threads (#533) * Add support for searching scrollback for specific posts/threads * Workaround ordering returned by GetPostThread API call --- bridge/bridge.go | 1 + bridge/mastodon/mastodon.go | 4 + bridge/mattermost/mattermost.go | 4 + bridge/slack/slack.go | 4 + mm-go-irckit/service.go | 134 +++++++++++++++++++++----------- 5 files changed, 102 insertions(+), 45 deletions(-) diff --git a/bridge/bridge.go b/bridge/bridge.go index 46c5ce9d..cda5c5a4 100644 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -51,6 +51,7 @@ type Bridger interface { GetPostsSince(channelID string, since int64) interface{} GetPosts(channelID string, limit int) interface{} + GetPostThread(postID string) interface{} SearchPosts(search string) interface{} ModifyPost(msgID, text string) error GetFileLinks(fileIDs []string) []string diff --git a/bridge/mastodon/mastodon.go b/bridge/mastodon/mastodon.go index 68dfbb17..71682fcb 100644 --- a/bridge/mastodon/mastodon.go +++ b/bridge/mastodon/mastodon.go @@ -202,6 +202,10 @@ func (m *Mastodon) GetPosts(channelID string, limit int) interface{} { return nil } +func (m *Mastodon) GetPostThread(postID string) interface{} { + return nil +} + func (m *Mastodon) GetChannelID(name, teamID string) string { return "" } diff --git a/bridge/mattermost/mattermost.go b/bridge/mattermost/mattermost.go index cd6c63ae..361c3555 100644 --- a/bridge/mattermost/mattermost.go +++ b/bridge/mattermost/mattermost.go @@ -1429,6 +1429,10 @@ func (m *Mattermost) GetPosts(channelID string, limit int) interface{} { return m.mc.GetPosts(channelID, limit) } +func (m *Mattermost) GetPostThread(postID string) interface{} { + return m.mc.GetPostThread(postID) +} + func (m *Mattermost) GetChannelID(name, teamID string) string { return m.mc.GetChannelID(name, teamID) } diff --git a/bridge/slack/slack.go b/bridge/slack/slack.go index 8fd8ff07..6b240d05 100644 --- a/bridge/slack/slack.go +++ b/bridge/slack/slack.go @@ -425,6 +425,10 @@ func (s *Slack) GetPosts(channelID string, limit int) interface{} { return nil } +func (s *Slack) GetPostThread(channelID string) interface{} { + return nil +} + func (s *Slack) GetChannelID(name, teamID string) string { return "" } diff --git a/mm-go-irckit/service.go b/mm-go-irckit/service.go index 974602ea..8f720e66 100644 --- a/mm-go-irckit/service.go +++ b/mm-go-irckit/service.go @@ -272,6 +272,20 @@ func searchUsers(u *User, toUser *User, args []string, service string) { } } +func getMattermostChannelName(u *User, channelID string) string { + channelName := u.br.GetChannelName(channelID) + channelMembers := strings.Split(channelName, "__") + + if len(channelMembers) != 2 { + return channelName + } + + if channelMembers[0] == u.br.GetMe().User { + return channelMembers[1] + } + return channelMembers[0] +} + //nolint:funlen,gocognit,gocyclo,cyclop func scrollback(u *User, toUser *User, args []string, service string) { if service == "slack" { @@ -280,27 +294,26 @@ func scrollback(u *User, toUser *User, args []string, service string) { } if len(args) != 2 { - u.MsgUser(toUser, "need SCROLLBACK (#|) ") + u.MsgUser(toUser, "need SCROLLBACK (#||) ") u.MsgUser(toUser, "e.g. SCROLLBACK #bugs 10 (show last 10 lines from #bugs)") return } + search := args[0] limit, err := strconv.Atoi(args[1]) if err != nil { - u.MsgUser(toUser, "need SCROLLBACK (#|) ") + u.MsgUser(toUser, "need SCROLLBACK (#||) ") u.MsgUser(toUser, "e.g. SCROLLBACK #bugs 10 (show last 10 lines from #bugs)") return } - var channelID string - var spoof func(string, string, ...int) - scrollbackUser, exists := u.Srv.HasUser(args[0]) + var channelID, searchPostID string + scrollbackUser, exists := u.Srv.HasUser(search) switch { - case strings.HasPrefix(args[0], "#"): - channelName := strings.ReplaceAll(args[0], "#", "") + case strings.HasPrefix(search, "#"): + channelName := strings.ReplaceAll(search, "#", "") channelID = u.br.GetChannelID(channelName, u.br.GetMe().TeamID) - spoof = u.Srv.Channel(channelID).SpoofMessage case exists && scrollbackUser.Ghost: // We need to sort the two user IDs to construct the DM // channel name. @@ -308,13 +321,20 @@ func scrollback(u *User, toUser *User, args []string, service string) { sort.Strings(userIDs) channelName := userIDs[0] + "__" + userIDs[1] channelID = u.br.GetChannelID(channelName, u.br.GetMe().TeamID) + case len(search) == 26: + searchPostID = search default: - u.MsgUser(toUser, "need SCROLLBACK (#|) ") + u.MsgUser(toUser, "need SCROLLBACK (#||) ") u.MsgUser(toUser, "e.g. SCROLLBACK #bugs 10 (show last 10 lines from #bugs)") return } - list := u.br.GetPosts(channelID, limit) + var list interface{} + if searchPostID != "" { + list = u.br.GetPostThread(searchPostID) + } else { + list = u.br.GetPosts(channelID, limit) + } if list == nil || list.(*model.PostList) == nil || len(list.(*model.PostList).Order) == 0 { u.MsgUser(toUser, "no results") return @@ -322,9 +342,27 @@ func scrollback(u *User, toUser *User, args []string, service string) { postlist, _ := list.(*model.PostList) - for i := len(postlist.Order) - 1; i >= 0; i-- { - p := postlist.Posts[postlist.Order[i]] - ts := time.Unix(0, p.CreateAt*int64(time.Millisecond)) + // Workaround https://github.com/mattermost/mattermost-server/issues/23081 + plOrder := postlist.Order + if searchPostID != "" { + plOrder = append(plOrder, searchPostID) + } + skipRoot := false + + for i := len(plOrder) - 1; i >= 0; i-- { + if limit != 0 && len(plOrder) > limit && i < len(plOrder)-limit { + continue + } + + p := postlist.Posts[plOrder[i]] + + // Workaround https://github.com/mattermost/mattermost-server/issues/23081 + if searchPostID != "" && p.Id == searchPostID { + if skipRoot { + continue + } + skipRoot = true + } props := p.GetProps() botname, override := props["override_username"].(string) @@ -338,49 +376,55 @@ func scrollback(u *User, toUser *User, args []string, service string) { nick = "system" } - for _, post := range strings.Split(p.Message, "\n") { - switch { // nolint:dupl - case (u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost" || u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost+post") && strings.HasPrefix(args[0], "#") && nick != "system": - threadMsgID := u.prefixContext("", p.Id, p.RootId, "scrollback") - scrollbackMsg := u.formatContextMessage(ts.Format("2006-01-02 15:04"), threadMsgID, post) - spoof(nick, scrollbackMsg) - case strings.HasPrefix(args[0], "#"): - scrollbackMsg := "[" + ts.Format("2006-01-02 15:04") + "] " + post - spoof(nick, scrollbackMsg) - case u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost" || u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost+post": - threadMsgID := u.prefixContext("", p.Id, p.RootId, "scrollback") - scrollbackMsg := u.formatContextMessage(ts.Format("2006-01-02 15:04"), threadMsgID, post) - u.MsgSpoofUser(scrollbackUser, nick, scrollbackMsg) - default: - scrollbackMsg := "[" + ts.Format("2006-01-02 15:04") + "]" + " <" + nick + "> " + post - u.MsgSpoofUser(scrollbackUser, nick, scrollbackMsg) + if searchPostID != "" && channelID == "" { + channelID = p.ChannelId + search = getMattermostChannelName(u, p.ChannelId) + if !strings.HasPrefix(search, "#") { + user := u.br.GetUser(search) + search = user.Nick + if override { + search = botname + } + scrollbackUser, _ = u.Srv.HasUser(search) } } + for _, post := range strings.Split(p.Message, "\n") { + formatScrollbackMsg(u, channelID, search, scrollbackUser, nick, p, post) + } + if len(p.FileIds) == 0 { continue } for _, fname := range u.br.GetFileLinks(p.FileIds) { fileMsg := "download file - " + fname - switch { // nolint:dupl - case (u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost" || u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost+post") && strings.HasPrefix(args[0], "#"): - threadMsgID := u.prefixContext("", p.Id, p.RootId, "scrollback_file") - scrollbackMsg := u.formatContextMessage(ts.Format("2006-01-02 15:04"), threadMsgID, fileMsg) - spoof(nick, scrollbackMsg) - case u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost" || u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost+post": - threadMsgID := u.prefixContext("", p.Id, p.RootId, "scrollback_file") - scrollbackMsg := u.formatContextMessage(ts.Format("2006-01-02 15:04"), threadMsgID, fileMsg) - u.MsgSpoofUser(scrollbackUser, nick, scrollbackMsg) - case strings.HasPrefix(args[0], "#"): - scrollbackMsg := "[" + ts.Format("2006-01-02 15:04") + "] " + fileMsg - spoof(nick, scrollbackMsg) - default: - scrollbackMsg := "[" + ts.Format("2006-01-02 15:04") + "]" + " <" + nick + "> " + fileMsg - u.MsgSpoofUser(scrollbackUser, nick, scrollbackMsg) - } + formatScrollbackMsg(u, channelID, search, scrollbackUser, nick, p, fileMsg) } } + + u.MsgUser(toUser, fmt.Sprintf("scrollback results shown in %s", search)) +} + +func formatScrollbackMsg(u *User, channelID string, channel string, user *User, nick string, p *model.Post, msgText string) { + ts := time.Unix(0, p.CreateAt*int64(time.Millisecond)) + + switch { + case (u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost" || u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost+post") && strings.HasPrefix(channel, "#") && nick != "system": //nolint:goconst + threadMsgID := u.prefixContext("", p.Id, p.RootId, "scrollback") + msg := u.formatContextMessage(ts.Format("2006-01-02 15:04"), threadMsgID, msgText) + u.Srv.Channel(channelID).SpoofMessage(nick, msg) + case strings.HasPrefix(channel, "#"): + msg := "[" + ts.Format("2006-01-02 15:04") + "] " + msgText + u.Srv.Channel(channelID).SpoofMessage(nick, msg) + case u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost" || u.v.GetString(u.br.Protocol()+".threadcontext") == "mattermost+post": + threadMsgID := u.prefixContext("", p.Id, p.RootId, "scrollback") + msg := u.formatContextMessage(ts.Format("2006-01-02 15:04"), threadMsgID, msgText) + u.MsgSpoofUser(user, nick, msg) + default: + msg := "[" + ts.Format("2006-01-02 15:04") + "]" + " <" + nick + "> " + msgText + u.MsgSpoofUser(user, nick, msg) + } } func updatelastviewed(u *User, toUser *User, args []string, service string) {