mirror of
https://github.com/42wim/matterbridge.git
synced 2024-12-18 17:12:37 +01:00
Preserve threading between Slack instances (#529)
* Opportunistically preserve Slack threading when parent thread in cache. [#529] * Removed slack-specific processing from gateway. * Added docs. * Add option to enable threading, with default to off. * Did cleanup on @42wim's comments. * Update gateway/gateway.go Co-Authored-By: patcon <patrick.c.connolly@gmail.com> * Suggestion from @42wim :) * Suggestions from @42wim. * More suggestions.
This commit is contained in:
parent
5666821e7b
commit
a20b7895a9
@ -37,6 +37,7 @@ Minecraft server chat support via [MatterLink](https://github.com/elytra/MatterL
|
|||||||
* [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols)
|
* [Support bridging between any protocols](https://github.com/42wim/matterbridge/wiki/Features#support-bridging-between-any-protocols)
|
||||||
* [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols)
|
* [Support multiple gateways(bridges) for your protocols](https://github.com/42wim/matterbridge/wiki/Features#support-multiple-gatewaysbridges-for-your-protocols)
|
||||||
* [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes)
|
* [Message edits and deletes](https://github.com/42wim/matterbridge/wiki/Features#message-edits-and-deletes)
|
||||||
|
* Preserves threading when possible
|
||||||
* [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling)
|
* [Attachment / files handling](https://github.com/42wim/matterbridge/wiki/Features#attachment--files-handling)
|
||||||
* [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing)
|
* [Username and avatar spoofing](https://github.com/42wim/matterbridge/wiki/Features#username-and-avatar-spoofing)
|
||||||
* [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups)
|
* [Private groups](https://github.com/42wim/matterbridge/wiki/Features#private-groups)
|
||||||
|
@ -35,6 +35,7 @@ type Message struct {
|
|||||||
Event string `json:"event"`
|
Event string `json:"event"`
|
||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
Gateway string `json:"gateway"`
|
Gateway string `json:"gateway"`
|
||||||
|
ParentID string `json:"parent_id"`
|
||||||
Timestamp time.Time `json:"timestamp"`
|
Timestamp time.Time `json:"timestamp"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Extra map[string][]interface{}
|
Extra map[string][]interface{}
|
||||||
@ -98,6 +99,7 @@ type Protocol struct {
|
|||||||
NoTLS bool // mattermost
|
NoTLS bool // mattermost
|
||||||
Password string // IRC,mattermost,XMPP,matrix
|
Password string // IRC,mattermost,XMPP,matrix
|
||||||
PrefixMessagesWithNick bool // mattemost, slack
|
PrefixMessagesWithNick bool // mattemost, slack
|
||||||
|
PreserveThreading bool // slack
|
||||||
Protocol string // all protocols
|
Protocol string // all protocols
|
||||||
QuoteDisable bool // telegram
|
QuoteDisable bool // telegram
|
||||||
QuoteFormat string // telegram
|
QuoteFormat string // telegram
|
||||||
|
@ -171,8 +171,10 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er
|
|||||||
Account: b.Account,
|
Account: b.Account,
|
||||||
ID: "slack " + ev.Timestamp,
|
ID: "slack " + ev.Timestamp,
|
||||||
Extra: map[string][]interface{}{},
|
Extra: map[string][]interface{}{},
|
||||||
|
ParentID: ev.ThreadTimestamp,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if b.useChannelID {
|
if b.useChannelID {
|
||||||
rmsg.Channel = "ID:" + channelInfo.ID
|
rmsg.Channel = "ID:" + channelInfo.ID
|
||||||
}
|
}
|
||||||
|
@ -310,6 +310,10 @@ func (b *Bslack) prepareMessageParameters(msg *config.Message) *slack.PostMessag
|
|||||||
params.Username = msg.Username
|
params.Username = msg.Username
|
||||||
params.LinkNames = 1 // replace mentions
|
params.LinkNames = 1 // replace mentions
|
||||||
params.IconURL = config.GetIconURL(msg, b.GetString(iconURLConfig))
|
params.IconURL = config.GetIconURL(msg, b.GetString(iconURLConfig))
|
||||||
|
msgFields := strings.Fields(msg.ParentID)
|
||||||
|
if len(msgFields) >= 2 {
|
||||||
|
params.ThreadTimestamp = msgFields[1]
|
||||||
|
}
|
||||||
if msg.Avatar != "" {
|
if msg.Avatar != "" {
|
||||||
params.IconURL = msg.Avatar
|
params.IconURL = msg.Avatar
|
||||||
}
|
}
|
||||||
|
@ -83,6 +83,25 @@ func New(cfg config.Gateway, r *Router) *Gateway {
|
|||||||
return gw
|
return gw
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the canonical ID that the message is keyed under in cache
|
||||||
|
func (gw *Gateway) FindCanonicalMsgID(mID string) string {
|
||||||
|
if gw.Messages.Contains(mID) {
|
||||||
|
return mID
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not keyed, iterate through cache for downstream, and infer upstream.
|
||||||
|
for _, mid := range gw.Messages.Keys() {
|
||||||
|
v, _ := gw.Messages.Peek(mid)
|
||||||
|
ids := v.([]*BrMsgID)
|
||||||
|
for _, downstreamMsgObj := range ids {
|
||||||
|
if mID == downstreamMsgObj.ID {
|
||||||
|
return mid.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
|
func (gw *Gateway) AddBridge(cfg *config.Bridge) error {
|
||||||
br := gw.Router.getBridge(cfg.Account)
|
br := gw.Router.getBridge(cfg.Account)
|
||||||
if br == nil {
|
if br == nil {
|
||||||
@ -206,6 +225,20 @@ func (gw *Gateway) getDestChannel(msg *config.Message, dest bridge.Bridge) []con
|
|||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gw *Gateway) getDestMsgID(msgID string, dest *bridge.Bridge, channel config.ChannelInfo) string {
|
||||||
|
if res, ok := gw.Messages.Get(msgID); ok {
|
||||||
|
IDs := res.([]*BrMsgID)
|
||||||
|
for _, id := range IDs {
|
||||||
|
// check protocol, bridge name and channelname
|
||||||
|
// for people that reuse the same bridge multiple times. see #342
|
||||||
|
if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
|
||||||
|
return id.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
|
func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrMsgID {
|
||||||
var brMsgIDs []*BrMsgID
|
var brMsgIDs []*BrMsgID
|
||||||
|
|
||||||
@ -242,6 +275,13 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
|
|||||||
return brMsgIDs
|
return brMsgIDs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the ID of the parent message in thread
|
||||||
|
var canonicalParentMsgID string
|
||||||
|
if msg.ParentID != "" && (gw.Config.General.PreserveThreading || dest.GetBool("PreserveThreading")) {
|
||||||
|
thisParentMsgID := dest.Protocol + " " + msg.ParentID
|
||||||
|
canonicalParentMsgID = gw.FindCanonicalMsgID(thisParentMsgID)
|
||||||
|
}
|
||||||
|
|
||||||
originchannel := msg.Channel
|
originchannel := msg.Channel
|
||||||
origmsg := msg
|
origmsg := msg
|
||||||
channels := gw.getDestChannel(&msg, *dest)
|
channels := gw.getDestChannel(&msg, *dest)
|
||||||
@ -258,28 +298,28 @@ func (gw *Gateway) handleMessage(msg config.Message, dest *bridge.Bridge) []*BrM
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
flog.Debugf("=> Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
|
flog.Debugf("=> Sending %#v from %s (%s) to %s (%s)", msg, msg.Account, originchannel, dest.Account, channel.Name)
|
||||||
|
|
||||||
msg.Channel = channel.Name
|
msg.Channel = channel.Name
|
||||||
msg.Avatar = gw.modifyAvatar(origmsg, dest)
|
msg.Avatar = gw.modifyAvatar(origmsg, dest)
|
||||||
msg.Username = gw.modifyUsername(origmsg, dest)
|
msg.Username = gw.modifyUsername(origmsg, dest)
|
||||||
msg.ID = ""
|
|
||||||
if res, ok := gw.Messages.Get(origmsg.ID); ok {
|
msg.ID = gw.getDestMsgID(origmsg.ID, dest, channel)
|
||||||
IDs := res.([]*BrMsgID)
|
|
||||||
for _, id := range IDs {
|
|
||||||
// check protocol, bridge name and channelname
|
|
||||||
// for people that reuse the same bridge multiple times. see #342
|
|
||||||
if dest.Protocol == id.br.Protocol && dest.Name == id.br.Name && channel.ID == id.ChannelID {
|
|
||||||
msg.ID = id.ID
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// for api we need originchannel as channel
|
// for api we need originchannel as channel
|
||||||
if dest.Protocol == "api" {
|
if dest.Protocol == "api" {
|
||||||
msg.Channel = originchannel
|
msg.Channel = originchannel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg.ParentID = gw.getDestMsgID(canonicalParentMsgID, dest, channel)
|
||||||
|
if msg.ParentID == "" {
|
||||||
|
msg.ParentID = canonicalParentMsgID
|
||||||
|
}
|
||||||
|
|
||||||
mID, err := dest.Send(msg)
|
mID, err := dest.Send(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
flog.Error(err)
|
flog.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
|
// append the message ID (mID) from this bridge (dest) to our brMsgIDs slice
|
||||||
if mID != "" {
|
if mID != "" {
|
||||||
flog.Debugf("mID %s: %s", dest.Account, mID)
|
flog.Debugf("mID %s: %s", dest.Account, mID)
|
||||||
|
@ -659,6 +659,12 @@ StripNick=false
|
|||||||
#OPTIONAL (default false)
|
#OPTIONAL (default false)
|
||||||
ShowTopicChange=false
|
ShowTopicChange=false
|
||||||
|
|
||||||
|
#Opportunistically preserve threaded replies between Slack channels.
|
||||||
|
#This only works if the parent message is still in the cache.
|
||||||
|
#Cache is flushed between restarts.
|
||||||
|
#OPTIONAL (default false)
|
||||||
|
PreserveThreading=false
|
||||||
|
|
||||||
###################################################################
|
###################################################################
|
||||||
#discord section
|
#discord section
|
||||||
###################################################################
|
###################################################################
|
||||||
|
Loading…
Reference in New Issue
Block a user