Files
mostlymatter/utils/markdown.go
Ahmad Karlam 081f4a5123 [MM-11143] Strip markdown formatting characters from push notifications (#17775)
* WIP: trim markdown for push notification

* fix: golangci

* fix: table regex

* fix: regex code block

* doc: add license

* WIP: custom renderer with goldmark

* WIP: update goldmark version to 1.38

* fix: use goldmark as parser

* fix: remove table extension

* fix: change buf to `strings.Builder`

* fix: return original string, log warning if error

* refactor: change `WriteString` to `WriteByte`

* refactor: change if condition

* refactor: use assertion

* refactor: move to inline

* fix: remove handle multiline

Already handled by mobile

* refactor: wrap same function

* refactor: move strip markdown to `sendPushNotificationSync`

* refactor: renaming variable aren't used

* fix: move log to func `sendPushNotificationSync`

* docs: add comment to `StripMarkdown`

* fix: move assign message to else

* appErr to err rename

Co-authored-by: Agniva De Sarker <agnivade@yahoo.co.in>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2021-06-29 08:54:13 +05:30

143 строки
4.1 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package utils
import (
"strings"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/ast"
"github.com/yuin/goldmark/extension"
astExt "github.com/yuin/goldmark/extension/ast"
"github.com/yuin/goldmark/renderer"
"github.com/yuin/goldmark/util"
)
// StripMarkdown remove some markdown syntax
func StripMarkdown(markdown string) (string, error) {
md := goldmark.New(
goldmark.WithExtensions(extension.Strikethrough),
goldmark.WithRenderer(
renderer.NewRenderer(renderer.WithNodeRenderers(
util.Prioritized(newNotificationRenderer(), 500),
)),
),
)
var buf strings.Builder
if err := md.Convert([]byte(markdown), &buf); err != nil {
return "", err
}
return strings.TrimSpace(buf.String()), nil
}
type notificationRenderer struct {
}
func newNotificationRenderer() *notificationRenderer {
return &notificationRenderer{}
}
func (r *notificationRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
// block
reg.Register(ast.KindDocument, r.renderDefault)
reg.Register(ast.KindHeading, r.renderItem)
reg.Register(ast.KindBlockquote, r.renderDefault)
reg.Register(ast.KindCodeBlock, r.renderCodeBlock)
reg.Register(ast.KindFencedCodeBlock, r.renderFencedCodeBlock)
reg.Register(ast.KindHTMLBlock, r.renderDefault)
reg.Register(ast.KindList, r.renderDefault)
reg.Register(ast.KindListItem, r.renderItem)
reg.Register(ast.KindParagraph, r.renderItem)
reg.Register(ast.KindTextBlock, r.renderTextBlock)
reg.Register(ast.KindThematicBreak, r.renderDefault)
// inlines
reg.Register(ast.KindAutoLink, r.renderDefault)
reg.Register(ast.KindCodeSpan, r.renderDefault)
reg.Register(ast.KindEmphasis, r.renderDefault)
reg.Register(ast.KindImage, r.renderDefault)
reg.Register(ast.KindLink, r.renderDefault)
reg.Register(ast.KindRawHTML, r.renderDefault)
reg.Register(ast.KindText, r.renderText)
reg.Register(ast.KindString, r.renderString)
// strikethrough
reg.Register(astExt.KindStrikethrough, r.renderDefault)
}
// renderDefault renderer function to renderDefault without changes
func (r *notificationRenderer) renderDefault(_ util.BufWriter, _ []byte, _ ast.Node, _ bool) (ast.WalkStatus, error) {
return ast.WalkContinue, nil
}
func (r *notificationRenderer) renderItem(w util.BufWriter, _ []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
if node.NextSibling() != nil {
_ = w.WriteByte(' ')
}
}
return ast.WalkContinue, nil
}
func (r *notificationRenderer) renderCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.CodeBlock)
if entering {
r.writeLines(w, source, n)
}
return ast.WalkContinue, nil
}
func (r *notificationRenderer) renderFencedCodeBlock(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
n := node.(*ast.FencedCodeBlock)
if entering {
r.writeLines(w, source, n)
}
return ast.WalkContinue, nil
}
func (r *notificationRenderer) renderText(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
n := node.(*ast.Text)
segment := n.Segment
_, _ = w.Write(segment.Value(source))
if !n.IsRaw() {
if n.HardLineBreak() || n.SoftLineBreak() {
_ = w.WriteByte('\n')
}
}
return ast.WalkContinue, nil
}
func (r *notificationRenderer) renderTextBlock(w util.BufWriter, _ []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
if _, ok := node.NextSibling().(ast.Node); ok && node.FirstChild() != nil {
_ = w.WriteByte(' ')
}
}
return ast.WalkContinue, nil
}
func (r *notificationRenderer) renderString(w util.BufWriter, _ []byte, node ast.Node, entering bool) (ast.WalkStatus, error) {
if !entering {
return ast.WalkContinue, nil
}
n := node.(*ast.String)
_, _ = w.Write(n.Value)
return ast.WalkContinue, nil
}
func (r *notificationRenderer) writeLines(w util.BufWriter, source []byte, n ast.Node) {
for i := 0; i < n.Lines().Len(); i++ {
line := n.Lines().At(i)
value := line.Value(source)
_, _ = w.Write(value)
}
}