diff --git a/bridge/config/config.go b/bridge/config/config.go index 18c6092082..2bb57bb4e6 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -181,6 +181,7 @@ type Protocol struct { type ChannelOptions struct { Key string // irc, xmpp WebhookURL string // discord + ForumID string // discord Topic string // zulip } diff --git a/bridge/discord/transmitter/transmitter.go b/bridge/discord/transmitter/transmitter.go index 71407a1d23..78c8ed2f24 100644 --- a/bridge/discord/transmitter/transmitter.go +++ b/bridge/discord/transmitter/transmitter.go @@ -63,13 +63,13 @@ func New(session *discordgo.Session, guild string, title string, autoCreate bool } // Send transmits a message to the given channel with the provided webhook data, and waits until Discord responds with message data. -func (t *Transmitter) Send(channelID string, params *discordgo.WebhookParams) (*discordgo.Message, error) { +func (t *Transmitter) SendThread(channelID string, threadID string, params *discordgo.WebhookParams) (*discordgo.Message, error) { wh, err := t.getOrCreateWebhook(channelID) if err != nil { return nil, err } - msg, err := t.session.WebhookExecute(wh.ID, wh.Token, true, params) + msg, err := t.session.WebhookThreadExecute(wh.ID, wh.Token, true, threadID, params) if err != nil { return nil, fmt.Errorf("execute failed: %w", err) } @@ -77,15 +77,24 @@ func (t *Transmitter) Send(channelID string, params *discordgo.WebhookParams) (* return msg, nil } +func (t *Transmitter) Send(channelID string, params *discordgo.WebhookParams) (*discordgo.Message, error) { + return t.SendThread(channelID, "", params) +} + // Edit will edit a message in a channel, if possible. -func (t *Transmitter) Edit(channelID string, messageID string, params *discordgo.WebhookParams) error { +func (t *Transmitter) EditThread(channelID string, threadID string, messageID string, params *discordgo.WebhookParams) error { wh := t.getWebhook(channelID) if wh == nil { return ErrWebhookNotFound } - uri := discordgo.EndpointWebhookToken(wh.ID, wh.Token) + "/messages/" + messageID + threadParam := "" + if threadID != "" { + threadParam = "?thread_id=" + threadID + } + + uri := discordgo.EndpointWebhookToken(wh.ID, wh.Token) + "/messages/" + messageID + threadParam _, err := t.session.RequestWithBucketID("PATCH", uri, params, discordgo.EndpointWebhookToken("", "")) if err != nil { return err @@ -94,6 +103,10 @@ func (t *Transmitter) Edit(channelID string, messageID string, params *discordgo return nil } +func (t *Transmitter) Edit(channelID string, messageID string, params *discordgo.WebhookParams) error { + return t.EditThread(channelID, "", messageID, params) +} + // HasWebhook checks whether the transmitter is using a particular webhook. func (t *Transmitter) HasWebhook(id string) bool { t.mutex.RLock() diff --git a/bridge/discord/webhook.go b/bridge/discord/webhook.go index b518ea6262..ec009a389d 100644 --- a/bridge/discord/webhook.go +++ b/bridge/discord/webhook.go @@ -42,10 +42,10 @@ func (b *Bdiscord) maybeGetLocalAvatar(msg *config.Message) string { return "" } -// webhookSend send one or more message via webhook, taking care of file -// uploads (from slack, telegram or mattermost). +// webhookSendThread send one or more message via webhook, taking care of file +// uploads (from slack, telegram or mattermost) and forum threads. // Returns messageID and error. -func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordgo.Message, error) { +func (b *Bdiscord) webhookSendThread(msg *config.Message, channelID string, threadID string) (*discordgo.Message, error) { var ( res *discordgo.Message res2 *discordgo.Message @@ -61,8 +61,9 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg // We can't send empty messages. if msg.Text != "" { - res, err = b.transmitter.Send( + res, err = b.transmitter.SendThread( channelID, + threadID, &discordgo.WebhookParams{ Content: msg.Text, Username: msg.Username, @@ -85,8 +86,9 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg } content := fi.Comment - res2, err = b.transmitter.Send( + res2, err = b.transmitter.SendThread( channelID, + threadID, &discordgo.WebhookParams{ Username: msg.Username, AvatarURL: msg.Avatar, @@ -108,6 +110,10 @@ func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordg return res, err } +func (b *Bdiscord) webhookSend(msg *config.Message, channelID string) (*discordgo.Message, error) { + return b.webhookSendThread(msg, channelID, "") +} + func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (string, error) { // skip events if msg.Event != "" && msg.Event != config.EventUserAction && msg.Event != config.EventJoinLeave && msg.Event != config.EventTopicChange { @@ -120,6 +126,15 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st return "", nil } + // If ForumID is set, it should be used as channelID for the webhook and the channelID is used as the thread_id + threadID := "" + if ci, ok := b.channelInfoMap[msg.Channel+b.Account]; ok { + if ci.Options.ForumID != "" { + threadID = channelID + channelID = ci.Options.ForumID + } + } + msg.Text = helper.ClipMessage(msg.Text, MessageLength, b.GetString("MessageClipped")) msg.Text = b.replaceUserMentions(msg.Text) // discord username must be [0..32] max @@ -129,7 +144,7 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st if msg.ID != "" { b.Log.Debugf("Editing webhook message") - err := b.transmitter.Edit(channelID, msg.ID, &discordgo.WebhookParams{ + err := b.transmitter.EditThread(channelID, threadID, msg.ID, &discordgo.WebhookParams{ Content: msg.Text, Username: msg.Username, AllowedMentions: b.getAllowedMentions(), @@ -141,7 +156,7 @@ func (b *Bdiscord) handleEventWebhook(msg *config.Message, channelID string) (st } b.Log.Debugf("Processing webhook sending for message %#v", msg) - discordMsg, err := b.webhookSend(msg, channelID) + discordMsg, err := b.webhookSendThread(msg, channelID, threadID) if err != nil { b.Log.Errorf("Could not broadcast via webhook for message %#v: %s", msg, err) return "", err diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample index 5932b269a3..b05df6614c 100644 --- a/matterbridge.toml.sample +++ b/matterbridge.toml.sample @@ -1928,8 +1928,13 @@ enable=true channel="mygreatgame" [gateway.inout.options] - # WebhookURL sends messages in the style of "puppets". You must configure a webhook URL for each channel you want to bridge. + # OPTIONAL - ForumID: the ID of the forum channel containing the thread. The thread ID should be used for "channel" then. + # Note: Do not add "ID:" at the beginning of the ForumID. + # Example: "123456789" + ForumID="" + # OPTIONAL - WebhookURL sends messages in the style of "puppets". You must configure a webhook URL for each channel you want to bridge. # If you have more than one channel and don't wnat to configure each channel manually, see the "AutoWebhooks" option in the gateway config. + # WebhookURL does not support forum channels (URLs containing "?thread_id="), see "ForumID" # Example: "https://discord.com/api/webhooks/1234/abcd_xyzw" WebhookURL=""