From aa247d29dcdbb20dcb99a6cfcc353115976f0803 Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Fri, 8 Mar 2024 00:21:07 +0100 Subject: [PATCH] Implement discord webhooks message splitting --- bridge/discord/webhook.go | 87 +++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 32 deletions(-) diff --git a/bridge/discord/webhook.go b/bridge/discord/webhook.go index b518ea6262..81786786b7 100644 --- a/bridge/discord/webhook.go +++ b/bridge/discord/webhook.go @@ -2,6 +2,7 @@ package bdiscord import ( "bytes" + "strings" "github.com/42wim/matterbridge/bridge/config" "github.com/42wim/matterbridge/bridge/helper" @@ -45,11 +46,12 @@ func (b *Bdiscord) maybeGetLocalAvatar(msg *config.Message) string { // webhookSend send one or more message via webhook, taking care of file // uploads (from slack, telegram or mattermost). // Returns messageID and error. -func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordgo.Message, error) { +func (b *Bdiscord) webhookSend(msg *config.Message, channelID string, clippingMessage string, splitMax int) (string, error) { var ( - res *discordgo.Message + res string = "" res2 *discordgo.Message err error + err2 error ) // If avatar is unset, mutate the message to include the local avatar (but only if settings say we should do this) @@ -61,17 +63,28 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg // We can't send empty messages. if msg.Text != "" { - res, err = b.transmitter.Send( - channelID, - &discordgo.WebhookParams{ - Content: msg.Text, - Username: msg.Username, - AvatarURL: msg.Avatar, - AllowedMentions: b.getAllowedMentions(), - }, - ) - if err != nil { - b.Log.Errorf("Could not send text (%s) for message %#v: %s", msg.Text, msg, err) + msgParts := helper.ClipOrSplitMessage(msg.Text, MessageLength, clippingMessage, splitMax) + var msgIds = []string{} + for _, msgPart := range msgParts { + partialRes, partialErr := b.transmitter.Send( + channelID, + &discordgo.WebhookParams{ + Content: msgPart, + Username: msg.Username, + AvatarURL: msg.Avatar, + AllowedMentions: b.getAllowedMentions(), + }, + ) + if partialErr != nil { + err = partialErr + break + } else { + msgIds = append(msgIds, partialRes.ID) + } + } + // Exploit that a discord message ID is actually just a large number, so we encode a list of IDs by separating them with ";". + if err == nil { + res = strings.Join(msgIds, ";") } } @@ -85,7 +98,7 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg } content := fi.Comment - res2, err = b.transmitter.Send( + res2, err2 = b.transmitter.Send( channelID, &discordgo.WebhookParams{ Username: msg.Username, @@ -95,14 +108,15 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg AllowedMentions: b.getAllowedMentions(), }, ) - if err != nil { + if err2 != nil { + err = err2 b.Log.Errorf("Could not send file %#v for message %#v: %s", file, msg, err) } } } if msg.Text == "" { - res = res2 + res = res2.ID } return res, err @@ -120,35 +134,44 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st return "", nil } - msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped")) - msg.Text = b.replaceUserMentions(msg.Text) // discord username must be [0..32] max if len(msg.Username) > 32 { msg.Username = msg.Username[0:32] } if msg.ID != "" { + // Exploit that a discord message ID is actually just a large number, and we encode a list of IDs by separating them with ";". + var msgIds = strings.Split(msg.ID, ";") + msgParts := helper.ClipOrSplitMessage(b.replaceUserMentions(msg.Text), MessageLength, b.GetString("MessageClipped"), len(msgIds)) + for len(msgParts) < len(msgIds) { + msgParts = append(msgParts, "((obsoleted by edit))") + } b.Log.Debugf("Editing webhook message") - err := b.transmitter.Edit(channelID, msg.ID, &discordgo.WebhookParams{ - Content: msg.Text, - Username: msg.Username, - AllowedMentions: b.getAllowedMentions(), - }) - if err == nil { + var edit_err error = nil + for i := range msgParts { + // In case of split-messages where some parts remain the same (i.e. only a typo-fix in a huge message), this causes some noop-updates. + // TODO: Optimize away noop-updates of un-edited messages + edit_err = b.transmitter.Edit(channelID, msgIds[i], &discordgo.WebhookParams{ + Content: msgParts[i], + Username: msg.Username, + AllowedMentions: b.getAllowedMentions(), + }) + if edit_err != nil { + break + } + } + if edit_err == nil { return msg.ID, nil } - b.Log.Errorf("Could not edit webhook message: %s", err) + b.Log.Errorf("Could not edit webhook message(s): %s; sending as new message(s) instead", edit_err) } b.Log.Debugf("Processing webhook sending for message %#v", msg) - discordMsg, err := b.webhookSend(msg, channelID) + msg.Text = b.replaceUserMentions(msg.Text) + msgId, err := b.webhookSend(msg, channelID, b.GetString("MessageClipped"), b.GetInt("MessageSplitMaxCount")) if err != nil { - b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msg, err) + b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msgId, err) return "", err } - if discordMsg == nil { - return "", nil - } - - return discordMsg.ID, nil + return msgId, nil }