From 5b715cd9e249e6851c5cf4c226b1c10a63447a13 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 30 Jan 2023 18:35:16 +0200 Subject: [PATCH] Allow inline links in Discord embed descriptions --- formatter.go | 31 +++++++++++++++++++++---------- portal.go | 14 +++++++------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/formatter.go b/formatter.go index f040173..ff99313 100644 --- a/formatter.go +++ b/formatter.go @@ -55,24 +55,35 @@ func (b *indentableParagraphParser) CanAcceptIndentedLine() bool { return true } +var removeFeaturesExceptLinks = []any{ + parser.NewListParser(), parser.NewListItemParser(), parser.NewHTMLBlockParser(), parser.NewRawHTMLParser(), + parser.NewSetextHeadingParser(), parser.NewATXHeadingParser(), parser.NewThematicBreakParser(), + parser.NewCodeBlockParser(), +} +var removeFeaturesAndLinks = append(removeFeaturesExceptLinks, parser.NewLinkParser()) +var fixIndentedParagraphs = goldmark.WithParserOptions(parser.WithBlockParsers(util.Prioritized(defaultIndentableParagraphParser, 500))) +var discordExtensions = goldmark.WithExtensions(extension.Strikethrough, mdext.SimpleSpoiler, mdext.DiscordUnderline, ExtDiscordEveryone, ExtDiscordTag) + var discordRenderer = goldmark.New( - goldmark.WithParser(mdext.ParserWithoutFeatures( - parser.NewListParser(), parser.NewListItemParser(), parser.NewHTMLBlockParser(), parser.NewRawHTMLParser(), - parser.NewSetextHeadingParser(), parser.NewATXHeadingParser(), parser.NewThematicBreakParser(), - parser.NewLinkParser(), parser.NewCodeBlockParser(), - )), - goldmark.WithParserOptions(parser.WithBlockParsers(util.Prioritized(defaultIndentableParagraphParser, 500))), - format.HTMLOptions, - goldmark.WithExtensions(extension.Strikethrough, mdext.SimpleSpoiler, mdext.DiscordUnderline, ExtDiscordEveryone, ExtDiscordTag), + goldmark.WithParser(mdext.ParserWithoutFeatures(removeFeaturesAndLinks...)), + fixIndentedParagraphs, format.HTMLOptions, discordExtensions, +) +var discordRendererWithInlineLinks = goldmark.New( + goldmark.WithParser(mdext.ParserWithoutFeatures(removeFeaturesExceptLinks...)), + fixIndentedParagraphs, format.HTMLOptions, discordExtensions, ) -func (portal *Portal) renderDiscordMarkdownOnlyHTML(text string) string { +func (portal *Portal) renderDiscordMarkdownOnlyHTML(text string, allowInlineLinks bool) string { text = escapeFixer.ReplaceAllStringFunc(text, escapeReplacement) var buf strings.Builder ctx := parser.NewContext() ctx.Set(parserContextPortal, portal) - err := discordRenderer.Convert([]byte(text), &buf, parser.WithContext(ctx)) + renderer := discordRenderer + if allowInlineLinks { + renderer = discordRendererWithInlineLinks + } + err := renderer.Convert([]byte(text), &buf, parser.WithContext(ctx)) if err != nil { panic(fmt.Errorf("markdown parser errored: %w", err)) } diff --git a/portal.go b/portal.go index 6a253a2..5a27787 100644 --- a/portal.go +++ b/portal.go @@ -779,7 +779,7 @@ func (portal *Portal) convertDiscordRichEmbed(intent *appservice.IntentAPI, embe } if embed.Title != "" { var titleHTML string - baseTitleHTML := portal.renderDiscordMarkdownOnlyHTML(embed.Title) + baseTitleHTML := portal.renderDiscordMarkdownOnlyHTML(embed.Title, false) if embed.URL != "" { titleHTML = fmt.Sprintf(embedHTMLTitleWithLink, html.EscapeString(embed.URL), baseTitleHTML) } else { @@ -788,7 +788,7 @@ func (portal *Portal) convertDiscordRichEmbed(intent *appservice.IntentAPI, embe htmlParts = append(htmlParts, titleHTML) } if embed.Description != "" { - htmlParts = append(htmlParts, fmt.Sprintf(embedHTMLDescription, portal.renderDiscordMarkdownOnlyHTML(embed.Description))) + htmlParts = append(htmlParts, fmt.Sprintf(embedHTMLDescription, portal.renderDiscordMarkdownOnlyHTML(embed.Description, true))) } for i := 0; i < len(embed.Fields); i++ { item := embed.Fields[i] @@ -805,15 +805,15 @@ func (portal *Portal) convertDiscordRichEmbed(intent *appservice.IntentAPI, embe headerParts := make([]string, len(splitItems)) contentParts := make([]string, len(splitItems)) for j, splitItem := range splitItems { - headerParts[j] = fmt.Sprintf(embedHTMLFieldName, portal.renderDiscordMarkdownOnlyHTML(splitItem.Name)) - contentParts[j] = fmt.Sprintf(embedHTMLFieldValue, portal.renderDiscordMarkdownOnlyHTML(splitItem.Value)) + headerParts[j] = fmt.Sprintf(embedHTMLFieldName, portal.renderDiscordMarkdownOnlyHTML(splitItem.Name, false)) + contentParts[j] = fmt.Sprintf(embedHTMLFieldValue, portal.renderDiscordMarkdownOnlyHTML(splitItem.Value, true)) } htmlParts = append(htmlParts, fmt.Sprintf(embedHTMLFields, strings.Join(headerParts, ""), strings.Join(contentParts, ""))) } else { htmlParts = append(htmlParts, fmt.Sprintf(embedHTMLLinearField, strconv.FormatBool(item.Inline), - portal.renderDiscordMarkdownOnlyHTML(item.Name), - portal.renderDiscordMarkdownOnlyHTML(item.Value), + portal.renderDiscordMarkdownOnlyHTML(item.Name, false), + portal.renderDiscordMarkdownOnlyHTML(item.Value, true), )) } } @@ -927,7 +927,7 @@ func (portal *Portal) convertDiscordTextMessage(intent *appservice.IntentAPI, ms htmlParts = append(htmlParts, fmt.Sprintf(msgInteractionTemplateHTML, puppet.MXID, puppet.Name, msg.Interaction.Name)) } if msg.Content != "" && !isPlainGifMessage(msg) { - htmlParts = append(htmlParts, portal.renderDiscordMarkdownOnlyHTML(msg.Content)) + htmlParts = append(htmlParts, portal.renderDiscordMarkdownOnlyHTML(msg.Content, false)) } previews := make([]*BeeperLinkPreview, 0) for i, embed := range msg.Embeds {