mirror of
https://github.com/42wim/matterbridge.git
synced 2024-11-15 00:19:24 +01:00
We create a new event EventFileDelete which will be used to delete specific uploaded files using the Extra["file"] in the config.Message. We also add a new NativeID key to the FileInfo struct which will contain the native file ID of the sending bridge. When a new file is added to the config.Message.Extra["file"] map, now the bridge native file ID should be added here. When the receiving bridge receives such a message, it should keep an internal mapping of NativeID <> bridge fileid/message id. In the case of discord we map it to the resulted discord message ID after uploading it. Now when a bridge deletes a file, it should send a EventFileDelete and setting the ID to the native file ID of the bridge. When the receiving bridge will get this event it'll look into the NativeID <> bridge id mapping to find their internal ID and use it to delete the specific file on their side. For now this is implemented for slack to discord but this will be add to other bridges where useful.
This commit is contained in:
parent
4b226a6a63
commit
6438a3dba3
@ -23,6 +23,7 @@ const (
|
|||||||
EventRejoinChannels = "rejoin_channels"
|
EventRejoinChannels = "rejoin_channels"
|
||||||
EventUserAction = "user_action"
|
EventUserAction = "user_action"
|
||||||
EventMsgDelete = "msg_delete"
|
EventMsgDelete = "msg_delete"
|
||||||
|
EventFileDelete = "file_delete"
|
||||||
EventAPIConnected = "api_connected"
|
EventAPIConnected = "api_connected"
|
||||||
EventUserTyping = "user_typing"
|
EventUserTyping = "user_typing"
|
||||||
EventGetChannelMembers = "get_channel_members"
|
EventGetChannelMembers = "get_channel_members"
|
||||||
@ -56,13 +57,14 @@ func (m Message) ParentValid() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type FileInfo struct {
|
type FileInfo struct {
|
||||||
Name string
|
Name string
|
||||||
Data *[]byte
|
Data *[]byte
|
||||||
Comment string
|
Comment string
|
||||||
URL string
|
URL string
|
||||||
Size int64
|
Size int64
|
||||||
Avatar bool
|
Avatar bool
|
||||||
SHA string
|
SHA string
|
||||||
|
NativeID string
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChannelInfo struct {
|
type ChannelInfo struct {
|
||||||
|
@ -10,10 +10,14 @@ import (
|
|||||||
"github.com/42wim/matterbridge/bridge/config"
|
"github.com/42wim/matterbridge/bridge/config"
|
||||||
"github.com/42wim/matterbridge/bridge/discord/transmitter"
|
"github.com/42wim/matterbridge/bridge/discord/transmitter"
|
||||||
"github.com/42wim/matterbridge/bridge/helper"
|
"github.com/42wim/matterbridge/bridge/helper"
|
||||||
|
lru "github.com/hashicorp/golang-lru"
|
||||||
"github.com/matterbridge/discordgo"
|
"github.com/matterbridge/discordgo"
|
||||||
)
|
)
|
||||||
|
|
||||||
const MessageLength = 1950
|
const (
|
||||||
|
MessageLength = 1950
|
||||||
|
cFileUpload = "file_upload"
|
||||||
|
)
|
||||||
|
|
||||||
type Bdiscord struct {
|
type Bdiscord struct {
|
||||||
*bridge.Config
|
*bridge.Config
|
||||||
@ -35,10 +39,20 @@ type Bdiscord struct {
|
|||||||
// Webhook specific logic
|
// Webhook specific logic
|
||||||
useAutoWebhooks bool
|
useAutoWebhooks bool
|
||||||
transmitter *transmitter.Transmitter
|
transmitter *transmitter.Transmitter
|
||||||
|
cache *lru.Cache
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *bridge.Config) bridge.Bridger {
|
func New(cfg *bridge.Config) bridge.Bridger {
|
||||||
b := &Bdiscord{Config: cfg}
|
newCache, err := lru.New(5000)
|
||||||
|
if err != nil {
|
||||||
|
cfg.Log.Fatalf("Could not create LRU cache: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &Bdiscord{
|
||||||
|
Config: cfg,
|
||||||
|
cache: newCache,
|
||||||
|
}
|
||||||
|
|
||||||
b.userMemberMap = make(map[string]*discordgo.Member)
|
b.userMemberMap = make(map[string]*discordgo.Member)
|
||||||
b.nickMemberMap = make(map[string]*discordgo.Member)
|
b.nickMemberMap = make(map[string]*discordgo.Member)
|
||||||
b.channelInfoMap = make(map[string]*config.ChannelInfo)
|
b.channelInfoMap = make(map[string]*config.ChannelInfo)
|
||||||
@ -280,6 +294,21 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete a file
|
||||||
|
if msg.Event == config.EventFileDelete {
|
||||||
|
if msg.ID == "" {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if fi, ok := b.cache.Get(cFileUpload + msg.ID); ok {
|
||||||
|
err := b.c.ChannelMessageDelete(channelID, fi.(string)) // nolint:forcetypeassert
|
||||||
|
b.cache.Remove(cFileUpload + msg.ID)
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("file %s not found", msg.ID)
|
||||||
|
}
|
||||||
|
|
||||||
// Upload a file if it exists
|
// Upload a file if it exists
|
||||||
if msg.Extra != nil {
|
if msg.Extra != nil {
|
||||||
for _, rmsg := range helper.HandleExtra(msg, b.General) {
|
for _, rmsg := range helper.HandleExtra(msg, b.General) {
|
||||||
@ -327,7 +356,6 @@ func (b *Bdiscord) handleEventBotUser(msg *config.Message, channelID string) (st
|
|||||||
|
|
||||||
// handleUploadFile handles native upload of files
|
// handleUploadFile handles native upload of files
|
||||||
func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (string, error) {
|
func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (string, error) {
|
||||||
var err error
|
|
||||||
for _, f := range msg.Extra["file"] {
|
for _, f := range msg.Extra["file"] {
|
||||||
fi := f.(config.FileInfo)
|
fi := f.(config.FileInfo)
|
||||||
file := discordgo.File{
|
file := discordgo.File{
|
||||||
@ -340,10 +368,15 @@ func (b *Bdiscord) handleUploadFile(msg *config.Message, channelID string) (stri
|
|||||||
Files: []*discordgo.File{&file},
|
Files: []*discordgo.File{&file},
|
||||||
AllowedMentions: b.getAllowedMentions(),
|
AllowedMentions: b.getAllowedMentions(),
|
||||||
}
|
}
|
||||||
_, err = b.c.ChannelMessageSendComplex(channelID, &m)
|
res, err := b.c.ChannelMessageSendComplex(channelID, &m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("file upload failed: %s", err)
|
return "", fmt.Errorf("file upload failed: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// link file_upload_nativeID (file ID from the original bridge) to our upload id
|
||||||
|
// so that we can remove this later when it eg needs to be deleted
|
||||||
|
b.cache.Add(cFileUpload+fi.NativeID, res.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
@ -168,17 +168,23 @@ func HandleDownloadSize(logger *logrus.Entry, msg *config.Message, name string,
|
|||||||
|
|
||||||
// HandleDownloadData adds the data for a remote file into a Matterbridge gateway message.
|
// HandleDownloadData adds the data for a remote file into a Matterbridge gateway message.
|
||||||
func HandleDownloadData(logger *logrus.Entry, msg *config.Message, name, comment, url string, data *[]byte, general *config.Protocol) {
|
func HandleDownloadData(logger *logrus.Entry, msg *config.Message, name, comment, url string, data *[]byte, general *config.Protocol) {
|
||||||
|
HandleDownloadData2(logger, msg, name, "", comment, url, data, general)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HandleDownloadData adds the data for a remote file into a Matterbridge gateway message.
|
||||||
|
func HandleDownloadData2(logger *logrus.Entry, msg *config.Message, name, id, comment, url string, data *[]byte, general *config.Protocol) {
|
||||||
var avatar bool
|
var avatar bool
|
||||||
logger.Debugf("Download OK %#v %#v", name, len(*data))
|
logger.Debugf("Download OK %#v %#v", name, len(*data))
|
||||||
if msg.Event == config.EventAvatarDownload {
|
if msg.Event == config.EventAvatarDownload {
|
||||||
avatar = true
|
avatar = true
|
||||||
}
|
}
|
||||||
msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{
|
msg.Extra["file"] = append(msg.Extra["file"], config.FileInfo{
|
||||||
Name: name,
|
Name: name,
|
||||||
Data: data,
|
Data: data,
|
||||||
URL: url,
|
URL: url,
|
||||||
Comment: comment,
|
Comment: comment,
|
||||||
Avatar: avatar,
|
Avatar: avatar,
|
||||||
|
NativeID: id,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,8 @@ func (b *Bslack) handleSlack() {
|
|||||||
b.Log.Debug("Start listening for Slack messages")
|
b.Log.Debug("Start listening for Slack messages")
|
||||||
for message := range messages {
|
for message := range messages {
|
||||||
// don't do any action on deleted/typing messages
|
// don't do any action on deleted/typing messages
|
||||||
if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete {
|
if message.Event != config.EventUserTyping && message.Event != config.EventMsgDelete &&
|
||||||
|
message.Event != config.EventFileDelete {
|
||||||
b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
|
b.Log.Debugf("<= Sending message from %s on %s to gateway", message.Username, b.Account)
|
||||||
// cleanup the message
|
// cleanup the message
|
||||||
message.Text = b.replaceMention(message.Text)
|
message.Text = b.replaceMention(message.Text)
|
||||||
@ -76,6 +77,13 @@ func (b *Bslack) handleSlackClient(messages chan *config.Message) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
messages <- rmsg
|
messages <- rmsg
|
||||||
|
case *slack.FileDeletedEvent:
|
||||||
|
rmsg, err := b.handleFileDeletedEvent(ev)
|
||||||
|
if err != nil {
|
||||||
|
b.Log.Errorf("%#v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
messages <- rmsg
|
||||||
case *slack.OutgoingErrorEvent:
|
case *slack.OutgoingErrorEvent:
|
||||||
b.Log.Debugf("%#v", ev.Error())
|
b.Log.Debugf("%#v", ev.Error())
|
||||||
case *slack.ChannelJoinedEvent:
|
case *slack.ChannelJoinedEvent:
|
||||||
@ -222,6 +230,26 @@ func (b *Bslack) handleMessageEvent(ev *slack.MessageEvent) (*config.Message, er
|
|||||||
return rmsg, nil
|
return rmsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Bslack) handleFileDeletedEvent(ev *slack.FileDeletedEvent) (*config.Message, error) {
|
||||||
|
if rawChannel, ok := b.cache.Get(cfileDownloadChannel + ev.FileID); ok {
|
||||||
|
channel, err := b.channels.getChannelByID(rawChannel.(string))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &config.Message{
|
||||||
|
Event: config.EventFileDelete,
|
||||||
|
Text: config.EventFileDelete,
|
||||||
|
Channel: channel.Name,
|
||||||
|
Account: b.Account,
|
||||||
|
ID: ev.FileID,
|
||||||
|
Protocol: b.Protocol,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("channel ID for file ID %s not found", ev.FileID)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) bool {
|
func (b *Bslack) handleStatusEvent(ev *slack.MessageEvent, rmsg *config.Message) bool {
|
||||||
switch ev.SubType {
|
switch ev.SubType {
|
||||||
case sChannelJoined, sMemberJoined:
|
case sChannelJoined, sMemberJoined:
|
||||||
@ -281,6 +309,8 @@ func (b *Bslack) handleAttachments(ev *slack.MessageEvent, rmsg *config.Message)
|
|||||||
|
|
||||||
// If we have files attached, download them (in memory) and put a pointer to it in msg.Extra.
|
// If we have files attached, download them (in memory) and put a pointer to it in msg.Extra.
|
||||||
for i := range ev.Files {
|
for i := range ev.Files {
|
||||||
|
// keep reference in cache on which channel we added this file
|
||||||
|
b.cache.Add(cfileDownloadChannel+ev.Files[i].ID, ev.Channel)
|
||||||
if err := b.handleDownloadFile(rmsg, &ev.Files[i], false); err != nil {
|
if err := b.handleDownloadFile(rmsg, &ev.Files[i], false); err != nil {
|
||||||
b.Log.Errorf("Could not download incoming file: %#v", err)
|
b.Log.Errorf("Could not download incoming file: %#v", err)
|
||||||
}
|
}
|
||||||
@ -330,7 +360,7 @@ func (b *Bslack) handleDownloadFile(rmsg *config.Message, file *slack.File, retr
|
|||||||
// that the comment is not duplicated.
|
// that the comment is not duplicated.
|
||||||
comment := rmsg.Text
|
comment := rmsg.Text
|
||||||
rmsg.Text = ""
|
rmsg.Text = ""
|
||||||
helper.HandleDownloadData(b.Log, rmsg, file.Name, comment, file.URLPrivateDownload, data, b.General)
|
helper.HandleDownloadData2(b.Log, rmsg, file.Name, file.ID, comment, file.URLPrivateDownload, data, b.General)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,24 +36,25 @@ type Bslack struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
sHello = "hello"
|
sHello = "hello"
|
||||||
sChannelJoin = "channel_join"
|
sChannelJoin = "channel_join"
|
||||||
sChannelLeave = "channel_leave"
|
sChannelLeave = "channel_leave"
|
||||||
sChannelJoined = "channel_joined"
|
sChannelJoined = "channel_joined"
|
||||||
sMemberJoined = "member_joined_channel"
|
sMemberJoined = "member_joined_channel"
|
||||||
sMessageChanged = "message_changed"
|
sMessageChanged = "message_changed"
|
||||||
sMessageDeleted = "message_deleted"
|
sMessageDeleted = "message_deleted"
|
||||||
sSlackAttachment = "slack_attachment"
|
sSlackAttachment = "slack_attachment"
|
||||||
sPinnedItem = "pinned_item"
|
sPinnedItem = "pinned_item"
|
||||||
sUnpinnedItem = "unpinned_item"
|
sUnpinnedItem = "unpinned_item"
|
||||||
sChannelTopic = "channel_topic"
|
sChannelTopic = "channel_topic"
|
||||||
sChannelPurpose = "channel_purpose"
|
sChannelPurpose = "channel_purpose"
|
||||||
sFileComment = "file_comment"
|
sFileComment = "file_comment"
|
||||||
sMeMessage = "me_message"
|
sMeMessage = "me_message"
|
||||||
sUserTyping = "user_typing"
|
sUserTyping = "user_typing"
|
||||||
sLatencyReport = "latency_report"
|
sLatencyReport = "latency_report"
|
||||||
sSystemUser = "system"
|
sSystemUser = "system"
|
||||||
sSlackBotUser = "slackbot"
|
sSlackBotUser = "slackbot"
|
||||||
|
cfileDownloadChannel = "file_download_channel"
|
||||||
|
|
||||||
tokenConfig = "Token"
|
tokenConfig = "Token"
|
||||||
incomingWebhookConfig = "WebhookBindAddress"
|
incomingWebhookConfig = "WebhookBindAddress"
|
||||||
|
@ -447,7 +447,10 @@ func (gw *Gateway) SendMessage(
|
|||||||
msg.Avatar = gw.modifyAvatar(rmsg, dest)
|
msg.Avatar = gw.modifyAvatar(rmsg, dest)
|
||||||
msg.Username = gw.modifyUsername(rmsg, dest)
|
msg.Username = gw.modifyUsername(rmsg, dest)
|
||||||
|
|
||||||
msg.ID = gw.getDestMsgID(rmsg.Protocol+" "+rmsg.ID, dest, channel)
|
// exclude file delete event as the msg ID here is the native file ID that needs to be deleted
|
||||||
|
if msg.Event != config.EventFileDelete {
|
||||||
|
msg.ID = gw.getDestMsgID(rmsg.Protocol+" "+rmsg.ID, dest, channel)
|
||||||
|
}
|
||||||
|
|
||||||
// for api we need originchannel as channel
|
// for api we need originchannel as channel
|
||||||
if dest.Protocol == apiProtocol {
|
if dest.Protocol == apiProtocol {
|
||||||
|
Loading…
Reference in New Issue
Block a user