From 9504f5e4e4c1070c42fb19fe54b60b769cf2180f Mon Sep 17 00:00:00 2001 From: Swirly <93798899+SwirlyStone5877@users.noreply.github.com> Date: Wed, 24 Jun 2026 12:56:02 -0500 Subject: [PATCH 1/2] Add support for formatting outgoing discord embeds (squashed commits, #234) Nothing like a force push to squash the commits... --- bridge/config/config.go | 1 + bridge/discord/handlers.go | 37 +++++++++++++++--------------- bridge/discord/handlers_test.go | 36 +++++++++++++++++++++++++---- docs/protocols/discord/settings.md | 12 ++++++++++ matterbridge.toml.sample | 3 +++ 5 files changed, 65 insertions(+), 24 deletions(-) diff --git a/bridge/config/config.go b/bridge/config/config.go index 24d0f2379c..8b3583fceb 100644 --- a/bridge/config/config.go +++ b/bridge/config/config.go @@ -141,6 +141,7 @@ type Protocol struct { EditSuffix string // mattermost, slack, discord, telegram EditDisable bool // mattermost, slack, discord, telegram EditMaxDays int // discord + EmbedFormat string // discord HTMLDisable bool // matrix IconURL string // mattermost, slack IgnoreFailureOnStart bool // general diff --git a/bridge/discord/handlers.go b/bridge/discord/handlers.go index 7f1f136dd4..514163287e 100644 --- a/bridge/discord/handlers.go +++ b/bridge/discord/handlers.go @@ -175,7 +175,7 @@ func (b *Bdiscord) messageCreate(s *discordgo.Session, m *discordgo.MessageCreat // if we have embedded content add it to text if b.GetBool("ShowEmbeds") && m.Message.Embeds != nil { for _, embed := range m.Message.Embeds { - rmsg.Text += handleEmbed(embed) + rmsg.Text += b.handleEmbed(embed) } } @@ -326,29 +326,28 @@ func (b *Bdiscord) memberRemove(s *discordgo.Session, m *discordgo.GuildMemberRe b.Remote <- rmsg } -func handleEmbed(embed *discordgo.MessageEmbed) string { - var t []string +func (b *Bdiscord) handleEmbed(embed *discordgo.MessageEmbed) string { var result string - t = append(t, embed.Title) - t = append(t, embed.Description) - t = append(t, embed.URL) + result = b.GetString("EmbedFormat") - i := 0 - for _, e := range t { - if e == "" { - continue - } - - i++ - if i == 1 { - result += " embed: " + e - continue - } - - result += " - " + e + if result == "" { + result = " embed: {TITLE} - {DESCRIPTION} - {URL}" } + result = strings.ReplaceAll(result, "{URL}", embed.URL) + result = strings.ReplaceAll(result, "{TITLE}", embed.Title) + result = strings.ReplaceAll(result, "{DESCRIPTION}", embed.Description) + result = strings.ReplaceAll(result, "{TIME}", embed.Timestamp) + // todo: make these work without making the entire thing panic + // result = strings.ReplaceAll(result, "{FOOTER}", embed.Footer.Text) + // result = strings.ReplaceAll(result, "{IMAGE}", embed.Image.URL) + // result = strings.ReplaceAll(result, "{THUMBNAIL}", embed.Thumbnail.URL) + // result = strings.ReplaceAll(result, "{VIDEO}", embed.Video.URL) + // result = strings.ReplaceAll(result, "{PROVIDER}", embed.Provider.URL) + // result = strings.ReplaceAll(result, "{AUTHOR}", embed.Author.Name) + // result = strings.ReplaceAll(result, "{AUTHORURL}", embed.Author.URL) + if result != "" { result += "\n" } diff --git a/bridge/discord/handlers_test.go b/bridge/discord/handlers_test.go index 915d9b19ce..776b5cbbc0 100644 --- a/bridge/discord/handlers_test.go +++ b/bridge/discord/handlers_test.go @@ -1,12 +1,38 @@ package bdiscord import ( + "strings" "testing" "github.com/bwmarrin/discordgo" "github.com/stretchr/testify/assert" ) +func handleEmbed(embed *discordgo.MessageEmbed) string { + var result string + + result = " embed: {TITLE} - {DESCRIPTION} - {URL}" + + result = strings.ReplaceAll(result, "{URL}", embed.URL) + result = strings.ReplaceAll(result, "{TITLE}", embed.Title) + result = strings.ReplaceAll(result, "{DESCRIPTION}", embed.Description) + result = strings.ReplaceAll(result, "{TIME}", embed.Timestamp) + // todo: make these work without making the entire thing panic + // result = strings.ReplaceAll(result, "{FOOTER}", embed.Footer.Text) + // result = strings.ReplaceAll(result, "{IMAGE}", embed.Image.URL) + // result = strings.ReplaceAll(result, "{THUMBNAIL}", embed.Thumbnail.URL) + // result = strings.ReplaceAll(result, "{VIDEO}", embed.Video.URL) + // result = strings.ReplaceAll(result, "{PROVIDER}", embed.Provider.URL) + // result = strings.ReplaceAll(result, "{AUTHOR}", embed.Author.Name) + // result = strings.ReplaceAll(result, "{AUTHORURL}", embed.Author.URL) + + if result != "" { + result += "\n" + } + + return result +} + func TestHandleEmbed(t *testing.T) { testcases := map[string]struct { embed *discordgo.MessageEmbed @@ -14,20 +40,20 @@ func TestHandleEmbed(t *testing.T) { }{ "allempty": { embed: &discordgo.MessageEmbed{}, - result: "", + result: " embed: - - \n", }, "one": { embed: &discordgo.MessageEmbed{ Title: "blah", }, - result: " embed: blah\n", + result: " embed: blah - - \n", }, "two": { embed: &discordgo.MessageEmbed{ Title: "blah", Description: "blah2", }, - result: " embed: blah - blah2\n", + result: " embed: blah - blah2 - \n", }, "three": { embed: &discordgo.MessageEmbed{ @@ -42,13 +68,13 @@ func TestHandleEmbed(t *testing.T) { Description: "blah2", URL: "blah3", }, - result: " embed: blah2 - blah3\n", + result: " embed: - blah2 - blah3\n", }, "oneb": { embed: &discordgo.MessageEmbed{ URL: "blah3", }, - result: " embed: blah3\n", + result: " embed: - - blah3\n", }, } diff --git a/docs/protocols/discord/settings.md b/docs/protocols/discord/settings.md index d055b6474b..af654cffa0 100644 --- a/docs/protocols/discord/settings.md +++ b/docs/protocols/discord/settings.md @@ -72,6 +72,18 @@ Shows title, description and URL of embedded messages (sent by other bots) ShowEmbeds=true ``` +## EmbedFormat + +Sets the format of the embeds attached to Discord messages sent by users (e.g. YouTube videos). +The only available placeholders are `{TITLE}`, `{URL}`, `{DESCRIPTION}`, and `{TIME}` (the title, page url, description, and timestamp respectively). If a parameter isn't in the originating embed it is simply omitted. + +- Setting: **OPTIONAL** (default `" embed: {TITLE} - {DESCRIPTION} - {URL}"`), **RELOADABLE** +- Format: *string* +- Example: + ```toml + EmbedFormat="\n# {TITLE} ({URL})\n{DESCRIPTION}\n{TIME}" + ``` + ## UseUserName Shows the username instead of the server nickname diff --git a/matterbridge.toml.sample b/matterbridge.toml.sample index 26b03d2f7d..f04d2cfba7 100644 --- a/matterbridge.toml.sample +++ b/matterbridge.toml.sample @@ -855,6 +855,9 @@ AllowMention=["everyone", "roles", "users"] # ShowEmbeds shows the title, description and URL of embedded messages (sent by other bots) ShowEmbeds=false +# EmbedFormat sets the outgoing format for messages sent by users with embeds (e.g. YouTube videos) +EmbedFormat="\n# {TITLE} ({URL})\n{DESCRIPTION}\n{TIME}" + # UseLocalAvatar specifies source bridges for which an avatar should be 'guessed' when an incoming message has no avatar. # This works by comparing the username of the message to an existing Discord user, and using the avatar of the Discord user. # From 654cc4d36e69846d7234937a880dd8c3dba0c440 Mon Sep 17 00:00:00 2001 From: Swirly Date: Wed, 24 Jun 2026 22:13:31 -0500 Subject: [PATCH 2/2] todon't --- bridge/discord/handlers.go | 46 ++++++++++++++++++++++++------ bridge/discord/handlers_test.go | 9 ------ docs/protocols/discord/settings.md | 4 +-- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/bridge/discord/handlers.go b/bridge/discord/handlers.go index 514163287e..5198f596b6 100644 --- a/bridge/discord/handlers.go +++ b/bridge/discord/handlers.go @@ -339,14 +339,44 @@ func (b *Bdiscord) handleEmbed(embed *discordgo.MessageEmbed) string { result = strings.ReplaceAll(result, "{TITLE}", embed.Title) result = strings.ReplaceAll(result, "{DESCRIPTION}", embed.Description) result = strings.ReplaceAll(result, "{TIME}", embed.Timestamp) - // todo: make these work without making the entire thing panic - // result = strings.ReplaceAll(result, "{FOOTER}", embed.Footer.Text) - // result = strings.ReplaceAll(result, "{IMAGE}", embed.Image.URL) - // result = strings.ReplaceAll(result, "{THUMBNAIL}", embed.Thumbnail.URL) - // result = strings.ReplaceAll(result, "{VIDEO}", embed.Video.URL) - // result = strings.ReplaceAll(result, "{PROVIDER}", embed.Provider.URL) - // result = strings.ReplaceAll(result, "{AUTHOR}", embed.Author.Name) - // result = strings.ReplaceAll(result, "{AUTHORURL}", embed.Author.URL) + + if embed.Footer != nil { + result = strings.ReplaceAll(result, "{FOOTER}", embed.Footer.Text) + } else { + result = strings.ReplaceAll(result, "{FOOTER}", "") + } + + if embed.Image != nil { + result = strings.ReplaceAll(result, "{IMAGE}", embed.Image.URL) + } else { + result = strings.ReplaceAll(result, "{IMAGE}", "") + } + + if embed.Thumbnail != nil { + result = strings.ReplaceAll(result, "{THUMBNAIL}", embed.Thumbnail.URL) + } else { + result = strings.ReplaceAll(result, "{THUMBNAIL}", "") + } + + if embed.Video != nil { + result = strings.ReplaceAll(result, "{VIDEO}", embed.Video.URL) + } else { + result = strings.ReplaceAll(result, "{VIDEO}", "") + } + + if embed.Provider != nil { + result = strings.ReplaceAll(result, "{PROVIDER}", embed.Provider.URL) + } else { + result = strings.ReplaceAll(result, "{PROVIDER}", "") + } + + if embed.Author != nil { + result = strings.ReplaceAll(result, "{AUTHOR}", embed.Author.Name) + result = strings.ReplaceAll(result, "{AUTHORURL}", embed.Author.URL) + } else { + result = strings.ReplaceAll(result, "{AUTHOR}", "") + result = strings.ReplaceAll(result, "{AUTHORURL}", "") + } if result != "" { result += "\n" diff --git a/bridge/discord/handlers_test.go b/bridge/discord/handlers_test.go index 776b5cbbc0..7115716f50 100644 --- a/bridge/discord/handlers_test.go +++ b/bridge/discord/handlers_test.go @@ -16,15 +16,6 @@ func handleEmbed(embed *discordgo.MessageEmbed) string { result = strings.ReplaceAll(result, "{URL}", embed.URL) result = strings.ReplaceAll(result, "{TITLE}", embed.Title) result = strings.ReplaceAll(result, "{DESCRIPTION}", embed.Description) - result = strings.ReplaceAll(result, "{TIME}", embed.Timestamp) - // todo: make these work without making the entire thing panic - // result = strings.ReplaceAll(result, "{FOOTER}", embed.Footer.Text) - // result = strings.ReplaceAll(result, "{IMAGE}", embed.Image.URL) - // result = strings.ReplaceAll(result, "{THUMBNAIL}", embed.Thumbnail.URL) - // result = strings.ReplaceAll(result, "{VIDEO}", embed.Video.URL) - // result = strings.ReplaceAll(result, "{PROVIDER}", embed.Provider.URL) - // result = strings.ReplaceAll(result, "{AUTHOR}", embed.Author.Name) - // result = strings.ReplaceAll(result, "{AUTHORURL}", embed.Author.URL) if result != "" { result += "\n" diff --git a/docs/protocols/discord/settings.md b/docs/protocols/discord/settings.md index af654cffa0..31efaa2f1c 100644 --- a/docs/protocols/discord/settings.md +++ b/docs/protocols/discord/settings.md @@ -75,13 +75,13 @@ Shows title, description and URL of embedded messages (sent by other bots) ## EmbedFormat Sets the format of the embeds attached to Discord messages sent by users (e.g. YouTube videos). -The only available placeholders are `{TITLE}`, `{URL}`, `{DESCRIPTION}`, and `{TIME}` (the title, page url, description, and timestamp respectively). If a parameter isn't in the originating embed it is simply omitted. +The only available placeholders are `{TITLE}`, `{URL}` (a URL), `{DESCRIPTION}`, `{TIME}`, `{FOOTER}`, `{IMAGE}` (a URL), `{THUMBNAIL}` (a URL), `{VIDEO}` (a URL), `{PROVIDER}` (a URL), `{AUTHOR}`, and `{AUTHORURL}` (a URL). If a parameter isn't in the originating embed it is simply omitted. (e.g. `Hello {AUTHOR}!` becomes `Hello !` if there is no author) - Setting: **OPTIONAL** (default `" embed: {TITLE} - {DESCRIPTION} - {URL}"`), **RELOADABLE** - Format: *string* - Example: ```toml - EmbedFormat="\n# {TITLE} ({URL})\n{DESCRIPTION}\n{TIME}" + EmbedFormat="\n# {TITLE} ({URL}) by {AUTHOR} ({AUTHORURL})\n{DESCRIPTION}\n{TIME}\n{FOOTER}" ``` ## UseUserName