Add support for disabling link previews via MSC4095

This commit is contained in:
Tulir Asokan
2025-08-10 23:47:39 +03:00
parent 52ebc21d9b
commit 820951cb6e
2 changed files with 38 additions and 5 deletions

View File

@@ -98,6 +98,7 @@ func (portal *Portal) renderDiscordMarkdownOnlyHTML(text string, allowInlineLink
const formatterContextPortalKey = "fi.mau.discord.portal" const formatterContextPortalKey = "fi.mau.discord.portal"
const formatterContextAllowedMentionsKey = "fi.mau.discord.allowed_mentions" const formatterContextAllowedMentionsKey = "fi.mau.discord.allowed_mentions"
const formatterContextInputAllowedMentionsKey = "fi.mau.discord.input_allowed_mentions" const formatterContextInputAllowedMentionsKey = "fi.mau.discord.input_allowed_mentions"
const formatterContextInputAllowedLinkPreviewsKey = "fi.mau.discord.input_allowed_link_previews"
func appendIfNotContains(arr []string, newItem string) []string { func appendIfNotContains(arr []string, newItem string) []string {
for _, item := range arr { for _, item := range arr {
@@ -221,16 +222,24 @@ var matrixHTMLParser = &format.HTMLParser{
return fmt.Sprintf("||%s||", text) return fmt.Sprintf("||%s||", text)
}, },
LinkConverter: func(text, href string, ctx format.Context) string { LinkConverter: func(text, href string, ctx format.Context) string {
linkPreviews := ctx.ReturnData[formatterContextInputAllowedLinkPreviewsKey].([]string)
allowPreview := linkPreviews == nil || slices.Contains(linkPreviews, href)
if text == href { if text == href {
if !allowPreview {
return fmt.Sprintf("<%s>", text)
}
return text return text
} else if !discordLinkRegexFull.MatchString(href) { } else if !discordLinkRegexFull.MatchString(href) {
return fmt.Sprintf("%s (%s)", escapeDiscordMarkdown(text), escapeDiscordMarkdown(href)) return fmt.Sprintf("%s (%s)", escapeDiscordMarkdown(text), escapeDiscordMarkdown(href))
} else if !allowPreview {
return fmt.Sprintf("[%s](<%s>)", escapeDiscordMarkdown(text), href)
} else {
return fmt.Sprintf("[%s](%s)", escapeDiscordMarkdown(text), href)
} }
return fmt.Sprintf("[%s](%s)", escapeDiscordMarkdown(text), href)
}, },
} }
func (portal *Portal) parseMatrixHTML(content *event.MessageEventContent) (string, *discordgo.MessageAllowedMentions) { func (portal *Portal) parseMatrixHTML(content *event.MessageEventContent, allowedLinkPreviews []string) (string, *discordgo.MessageAllowedMentions) {
allowedMentions := &discordgo.MessageAllowedMentions{ allowedMentions := &discordgo.MessageAllowedMentions{
Parse: []discordgo.AllowedMentionType{}, Parse: []discordgo.AllowedMentionType{},
Users: []string{}, Users: []string{},
@@ -238,6 +247,7 @@ func (portal *Portal) parseMatrixHTML(content *event.MessageEventContent) (strin
} }
if content.Format == event.FormatHTML && len(content.FormattedBody) > 0 { if content.Format == event.FormatHTML && len(content.FormattedBody) > 0 {
ctx := format.NewContext() ctx := format.NewContext()
ctx.ReturnData[formatterContextInputAllowedLinkPreviewsKey] = allowedLinkPreviews
ctx.ReturnData[formatterContextPortalKey] = portal ctx.ReturnData[formatterContextPortalKey] = portal
ctx.ReturnData[formatterContextAllowedMentionsKey] = allowedMentions ctx.ReturnData[formatterContextAllowedMentionsKey] = allowedMentions
if content.Mentions != nil { if content.Mentions != nil {

View File

@@ -1544,7 +1544,8 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
if editMXID := content.GetRelatesTo().GetReplaceID(); editMXID != "" && content.NewContent != nil { if editMXID := content.GetRelatesTo().GetReplaceID(); editMXID != "" && content.NewContent != nil {
edits := portal.bridge.DB.Message.GetByMXID(portal.Key, editMXID) edits := portal.bridge.DB.Message.GetByMXID(portal.Key, editMXID)
if edits != nil { if edits != nil {
discordContent, allowedMentions := portal.parseMatrixHTML(content.NewContent) newContentRaw, _ := evt.Content.Raw["m.new_content"].(map[string]any)
discordContent, allowedMentions := portal.parseMatrixHTML(content.NewContent, parseAllowedLinkPreviews(newContentRaw))
var err error var err error
var msg *discordgo.Message var msg *discordgo.Message
if !isWebhookSend { if !isWebhookSend {
@@ -1623,7 +1624,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
} }
switch content.MsgType { switch content.MsgType {
case event.MsgText, event.MsgEmote, event.MsgNotice: case event.MsgText, event.MsgEmote, event.MsgNotice:
sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content) sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content, parseAllowedLinkPreviews(evt.Content.Raw))
if content.MsgType == event.MsgEmote { if content.MsgType == event.MsgEmote {
sendReq.Content = fmt.Sprintf("_%s_", sendReq.Content) sendReq.Content = fmt.Sprintf("_%s_", sendReq.Content)
} }
@@ -1636,7 +1637,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
filename := content.Body filename := content.Body
if content.FileName != "" && content.FileName != content.Body { if content.FileName != "" && content.FileName != content.Body {
filename = content.FileName filename = content.FileName
sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content) sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content, parseAllowedLinkPreviews(evt.Content.Raw))
} }
if evt.Content.Raw["page.codeberg.everypizza.msc4193.spoiler"] == true { if evt.Content.Raw["page.codeberg.everypizza.msc4193.spoiler"] == true {
@@ -1744,6 +1745,28 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
} }
} }
func parseAllowedLinkPreviews(raw map[string]any) []string {
if raw == nil {
return nil
}
linkPreviews, ok := raw["com.beeper.linkpreviews"].([]any)
if !ok {
return nil
}
allowedLinkPreviews := make([]string, 0, len(linkPreviews))
for _, preview := range linkPreviews {
previewMap, ok := preview.(map[string]any)
if !ok {
continue
}
matchedURL, _ := previewMap["matched_url"].(string)
if matchedURL != "" {
allowedLinkPreviews = append(allowedLinkPreviews, matchedURL)
}
}
return allowedLinkPreviews
}
func (portal *Portal) sendDeliveryReceipt(eventID id.EventID) { func (portal *Portal) sendDeliveryReceipt(eventID id.EventID) {
if portal.bridge.Config.Bridge.DeliveryReceipts { if portal.bridge.Config.Bridge.DeliveryReceipts {
err := portal.bridge.Bot.MarkRead(portal.MXID, eventID) err := portal.bridge.Bot.MarkRead(portal.MXID, eventID)