Skip to content
Snippets Groups Projects
Unverified Commit e7f7f50e authored by Sumner Evans's avatar Sumner Evans
Browse files

connector/linkedinfmt: sort attributes before formatting and use codepoints

parent 14a42071
No related branches found
No related tags found
No related merge requests found
......@@ -32,8 +32,6 @@ func (*LinkedInConnector) GetBridgeInfoVersion() (info, capabilities int) {
return 1, 1
}
// TODO get these from getConfig instead of hardcoding?
const MaxTextLength = 4096
const MaxCaptionLength = 1024
const MaxFileSize = 2 * 1024 * 1024 * 1024
......
{
"_type": "com.linkedin.pemberly.text.AttributedText",
"attributes": [
{
"start": 0,
"length": 8,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 8,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": {
"__typename": "pemberly_text_LineBreak",
"_type": "com.linkedin.pemberly.text.LineBreak",
"_recipeType": "com.linkedin.9c282b4fb14f174ee0396ed27d7bc71d"
},
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 8,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 9,
"length": 37,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 46,
"length": 22,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 68,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": {
"__typename": "pemberly_text_LineBreak",
"_type": "com.linkedin.pemberly.text.LineBreak",
"_recipeType": "com.linkedin.9c282b4fb14f174ee0396ed27d7bc71d"
},
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 68,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 81,
"length": 28,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": {
"__typename": "pemberly_text_Bold",
"_type": "com.linkedin.pemberly.text.Bold",
"_recipeType": "com.linkedin.e545ef8a692fdb8327d2709264ebfd4c"
},
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 69,
"length": 107,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 176,
"length": 24,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 200,
"length": 38,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 238,
"length": 33,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 271,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": {
"__typename": "pemberly_text_LineBreak",
"_type": "com.linkedin.pemberly.text.LineBreak",
"_recipeType": "com.linkedin.9c282b4fb14f174ee0396ed27d7bc71d"
},
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 271,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 274,
"length": 18,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": {
"__typename": "pemberly_text_Bold",
"_type": "com.linkedin.pemberly.text.Bold",
"_recipeType": "com.linkedin.e545ef8a692fdb8327d2709264ebfd4c"
},
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 300,
"length": 34,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": {
"__typename": "pemberly_text_Bold",
"_type": "com.linkedin.pemberly.text.Bold",
"_recipeType": "com.linkedin.e545ef8a692fdb8327d2709264ebfd4c"
},
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 272,
"length": 102,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 374,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": {
"__typename": "pemberly_text_LineBreak",
"_type": "com.linkedin.pemberly.text.LineBreak",
"_recipeType": "com.linkedin.9c282b4fb14f174ee0396ed27d7bc71d"
},
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 374,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 377,
"length": 8,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": {
"__typename": "pemberly_text_Bold",
"_type": "com.linkedin.pemberly.text.Bold",
"_recipeType": "com.linkedin.e545ef8a692fdb8327d2709264ebfd4c"
},
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 375,
"length": 75,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 450,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"hyperlink": null,
"listItem": null,
"paragraph": null,
"lineBreak": {
"__typename": "pemberly_text_LineBreak",
"_type": "com.linkedin.pemberly.text.LineBreak",
"_recipeType": "com.linkedin.9c282b4fb14f174ee0396ed27d7bc71d"
},
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 450,
"length": 1,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
},
{
"start": 451,
"length": 39,
"_type": "com.linkedin.pemberly.text.Attribute",
"_recipeType": "com.linkedin.be0e5acdecfe12dada0041f3d644cfa4",
"attributeKind": {
"paragraph": {
"__typename": "pemberly_text_Paragraph",
"_type": "com.linkedin.pemberly.text.Paragraph",
"_recipeType": "com.linkedin.bb8b9f6bc8796ae251eca1334b3f092a"
},
"hyperlink": null,
"listItem": null,
"lineBreak": null,
"subscript": null,
"underline": null,
"superscript": null,
"bold": null,
"list": null,
"italic": null,
"entity": null
}
}
],
"text": "Hi Test!\nChasing leads that aren't converting?There’s a smarter way!\nIntroducing Simplia’s $15M Ai+™ platform, built by ex-Google and Meta experts to supercharge your lead gen:\uD83D\uDE80 10X more leads, faster⏱ Fully automated—even while you sleep❌ No tech or credit card required\n\uD83C\uDF81 Limited Time Offer: Get a FREE Custom AI Plan (worth $5,000) just for showing up to a quick meeting.\n⚠ Act fast: Only 5 businesses can claim this offer—will you be one of them?\n\uD83D\uDC47 Start generating smarter leads today!",
"_recipeType": "com.linkedin.e42015befb40f6eae45546693a8cac36"
}
{
"msgtype": "m.text",
"m.mentions": {},
"body":"Hi Test!\nChasing leads that aren't converting?There’s a smarter way!\nIntroducing Simplia’s $15M Ai+™ platform, built by ex-Google and Meta experts to supercharge your lead gen:\uD83D\uDE80 10X more leads, faster⏱ Fully automated—even while you sleep❌ No tech or credit card required\n\uD83C\uDF81 Limited Time Offer: Get a FREE Custom AI Plan (worth $5,000) just for showing up to a quick meeting.\n⚠ Act fast: Only 5 businesses can claim this offer—will you be one of them?\n\uD83D\uDC47 Start generating smarter leads today!",
"format": "org.matrix.custom.html",
"formatted_body":"<p>Hi Test!</p><br><p>Chasing leads that aren&#39;t converting?</p><p>There’s a smarter way!</p><br><p>Introducing <strong>Simplia’s $15M Ai+™ platform</strong>, built by ex-Google and Meta experts to supercharge your lead gen:</p><p>🚀 10X more leads, faster</p><p>⏱ Fully automated—even while you sleep</p><p>❌ No tech or credit card required</p><br><p>\uD83C\uDF81 <strong>Limited Time Offer</strong>: Get a <strong>FREE Custom AI Plan (worth $5,000)</strong> just for showing up to a quick meeting.</p><br><p>⚠ <strong>Act fast</strong>: Only 5 businesses can claim this offer—will you be one of them?</p><br><p>👇 Start generating smarter leads today!</p>"
}
package linkedinfmt_test
import (
"context"
"embed"
"encoding/json"
"io"
"path"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"maunium.net/go/mautrix/id"
"go.mau.fi/mautrix-linkedin/pkg/connector/linkedinfmt"
"go.mau.fi/mautrix-linkedin/pkg/linkedingo"
)
//go:embed attributedtext/*
var attributedtextFS embed.FS
var linkedinFmtParams = linkedinfmt.FormatParams{
GetMXIDByURN: func(ctx context.Context, entityURN linkedingo.URN) (id.UserID, error) {
return id.UserID(entityURN.ID()), nil
},
}
func TestParse(t *testing.T) {
entries, err := attributedtextFS.ReadDir("attributedtext")
require.NoError(t, err)
for _, entry := range entries {
if strings.HasSuffix(entry.Name(), "_msg.json") {
continue
}
t.Run(entry.Name(), func(t *testing.T) {
f, err := attributedtextFS.Open(path.Join("attributedtext", entry.Name()))
require.NoError(t, err)
expectedMsgJSONFile, err := attributedtextFS.Open(path.Join("attributedtext", strings.TrimSuffix(entry.Name(), ".json")+"_msg.json"))
require.NoError(t, err)
expectedMsgJSON, err := io.ReadAll(expectedMsgJSONFile)
require.NoError(t, err)
var attributedText linkedingo.AttributedText
err = json.NewDecoder(f).Decode(&attributedText)
assert.NoError(t, err)
content, err := linkedinfmt.Parse(context.TODO(), attributedText.Text, attributedText.Attributes, linkedinFmtParams)
assert.NoError(t, err)
marshalled, err := json.Marshal(content)
assert.NoError(t, err)
assert.JSONEq(t, string(expectedMsgJSON), string(marshalled))
})
}
}
......@@ -19,6 +19,7 @@ package linkedinfmt
import (
"context"
"html"
"slices"
"github.com/rs/zerolog"
"golang.org/x/exp/maps"
......@@ -62,8 +63,14 @@ func Parse(ctx context.Context, message string, attributes []linkedingo.Attribut
lrt := &LinkedRangeTree{}
mentions := map[id.UserID]struct{}{}
utf16Message := NewUTF16String(message)
maxLength := len(utf16Message)
charArr := []rune(message)
maxLength := len(charArr)
slices.SortFunc(attributes, func(l, r linkedingo.Attribute) int {
if l.Start-r.Start == 0 {
return r.Length - l.Length
}
return l.Start - r.Start
})
for _, a := range attributes {
br := BodyRange{
Start: a.Start,
......@@ -82,7 +89,7 @@ func Parse(ctx context.Context, message string, attributes []linkedingo.Attribut
Msg("Failed to get user info for mention")
continue // Skip this mention
}
userInfo.Name = utf16Message[a.Start+1 : a.Start+a.Length].String()
userInfo.Name = string(charArr[a.Start+1 : a.Start+a.Length])
mentions[userInfo.MXID] = struct{}{}
br.Value = Mention{userInfo, networkid.UserID(urn.ID())}
case a.AttributeKind.Hyperlink != nil:
......@@ -110,7 +117,7 @@ func Parse(ctx context.Context, message string, attributes []linkedingo.Attribut
}
content.Mentions.UserIDs = maps.Keys(mentions)
content.FormattedBody = lrt.Format(utf16Message, formatContext{})
content.FormattedBody = lrt.Format(charArr, formatContext{})
content.Format = event.FormatHTML
return content, nil
}
......@@ -19,7 +19,6 @@ package linkedinfmt
import (
"fmt"
"strings"
"unicode/utf16"
)
func (m Mention) Format(message string) string {
......@@ -60,22 +59,12 @@ func (s Style) Format(message string) string {
}
}
type UTF16String []uint16
func NewUTF16String(s string) UTF16String {
return utf16.Encode([]rune(s))
}
func (u UTF16String) String() string {
return string(utf16.Decode(u))
}
func (lrt *LinkedRangeTree) Format(message UTF16String, ctx formatContext) string {
func (lrt *LinkedRangeTree) Format(message []rune, ctx formatContext) string {
if lrt == nil || lrt.Node == nil {
return ctx.TextToHTML(message.String())
return ctx.TextToHTML(string(message))
}
head := message[:lrt.Node.Start]
headStr := ctx.TextToHTML(head.String())
headStr := ctx.TextToHTML(string(head))
inner := message[lrt.Node.Start:lrt.Node.End()]
tail := message[lrt.Node.End():]
ourCtx := ctx
......
......@@ -90,7 +90,7 @@ func Parse(ctx context.Context, parser *HTMLParser, content *event.MessageEventC
if parsed == nil {
return
}
body.Text = parsed.String.String()
body.Text = string(parsed.String)
body.Attributes = make([]linkedingo.SendMessageAttribute, len(parsed.Entities))
for i, ent := range parsed.Entities {
body.Attributes[i] = toLinkedInAttribute(ent)
......
......@@ -31,7 +31,7 @@ import (
)
type EntityString struct {
String linkedinfmt.UTF16String
String []rune
Entities linkedinfmt.BodyRangeList
}
......@@ -40,11 +40,11 @@ var DebugLog = func(format string, args ...any) {}
func NewEntityString(val string) *EntityString {
DebugLog("NEW %q\n", val)
return &EntityString{
String: linkedinfmt.NewUTF16String(val),
String: []rune(val),
}
}
func (es *EntityString) Split(at uint16) []*EntityString {
func (es *EntityString) Split(at rune) []*EntityString {
if at > 0x7F {
panic("cannot split at non-ASCII character")
}
......@@ -129,14 +129,14 @@ func (es *EntityString) TrimSpace() *EntityString {
}
func JoinEntityString(with string, strings ...*EntityString) *EntityString {
withUTF16 := linkedinfmt.NewUTF16String(with)
withUTF16 := []rune(with)
totalLen := 0
totalEntities := 0
for _, s := range strings {
totalLen += len(s.String)
totalEntities += len(s.Entities)
}
str := make(linkedinfmt.UTF16String, 0, totalLen+len(strings)*len(withUTF16))
str := make([]rune, 0, totalLen+len(strings)*len(withUTF16))
entities := make(linkedinfmt.BodyRangeList, 0, totalEntities)
DebugLog("JOIN %q %d\n", with, len(strings))
for _, s := range strings {
......@@ -195,7 +195,7 @@ func (es *EntityString) AppendString(other string) *EntityString {
return es
}
DebugLog("APPENDSTRING %q %+v\n + %q\n", es.String, es.Entities, other)
es.String = append(es.String, linkedinfmt.NewUTF16String(other)...)
es.String = append(es.String, []rune(other)...)
DebugLog(" -> %q %+v\n", es.String, es.Entities)
return es
}
......@@ -319,7 +319,7 @@ func (parser *HTMLParser) linkToString(node *html.Node, ctx Context) *EntityStri
if len(href) == 0 {
return str
}
ent := NewEntityString(str.String.String())
ent := NewEntityString(string(str.String))
parsedMatrix, err := id.ParseMatrixURIOrMatrixToURL(href)
if err == nil && parsedMatrix != nil && parsedMatrix.Sigil1 == '@' {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment