diff --git a/pkg/connector/msgconv/mentions.go b/pkg/connector/msgconv/mentions.go index 3f68d18..0f14d29 100644 --- a/pkg/connector/msgconv/mentions.go +++ b/pkg/connector/msgconv/mentions.go @@ -18,6 +18,7 @@ package msgconv import ( "context" + "regexp" "slices" //"log" @@ -42,6 +43,14 @@ func (u UTF16String) String() string { return string(utf16.Decode(u)) } +var ( + META_BOLD_REGEX = regexp.MustCompile(`\*([^*]+)\*`) + META_ITALIC_REGEX = regexp.MustCompile(`_([^_]+)_`) + META_STRIKE_REGEX = regexp.MustCompile(`~([^~]+)~`) + META_MONOSPACE_REGEX = regexp.MustCompile("`([^`]+)`") + META_MONOSPACE_BLOCK_REGEX = regexp.MustCompile("```([^`]+)```") +) + func (mc *MessageConverter) metaToMatrixText(ctx context.Context, text string, rawMentions *socket.MentionData, portal *bridgev2.Portal) (content *event.MessageEventContent) { content = &event.MessageEventContent{ MsgType: event.MsgText, @@ -52,53 +61,61 @@ func (mc *MessageConverter) metaToMatrixText(ctx context.Context, text string, r if err != nil { zerolog.Ctx(ctx).Err(err).Msg("Failed to parse mentions") } - if mentions == nil { - return - } - utf16Text := NewUTF16String(text) - prevEnd := 0 - var output strings.Builder - for _, mention := range mentions { - if mention.Offset < prevEnd { - zerolog.Ctx(ctx).Warn().Msg("Ignoring overlapping mentions in message") - continue - } else if mention.Offset >= len(utf16Text) { - zerolog.Ctx(ctx).Warn().Msg("Ignoring mention outside of message") - continue - } - end := mention.Offset + mention.Length - if end > len(utf16Text) { - end = len(utf16Text) - } - var mentionLink string - switch mention.Type { - case socket.MentionTypePerson: - info, err := mc.getBasicUserInfo(ctx, portal, ids.MakeUserID(mention.ID)) - if err != nil { - zerolog.Ctx(ctx).Err(err).Msg("Failed to get user info for mention") + + outputString := text + + if mentions != nil { + utf16Text := NewUTF16String(text) + prevEnd := 0 + var output strings.Builder + for _, mention := range mentions { + if mention.Offset < prevEnd { + zerolog.Ctx(ctx).Warn().Msg("Ignoring overlapping mentions in message") + continue + } else if mention.Offset >= len(utf16Text) { + zerolog.Ctx(ctx).Warn().Msg("Ignoring mention outside of message") continue } - if !slices.Contains(content.Mentions.UserIDs, info.MXID) { - content.Mentions.UserIDs = append(content.Mentions.UserIDs, info.MXID) + end := mention.Offset + mention.Length + if end > len(utf16Text) { + end = len(utf16Text) } - mentionLink = info.MXID.URI().MatrixToURL() - case socket.MentionTypeThread: - // TODO: how does one send thread mentions? - } - if mentionLink == "" { - continue + var mentionLink string + switch mention.Type { + case socket.MentionTypePerson: + info, err := mc.getBasicUserInfo(ctx, portal, ids.MakeUserID(mention.ID)) + if err != nil { + zerolog.Ctx(ctx).Err(err).Msg("Failed to get user info for mention") + continue + } + if !slices.Contains(content.Mentions.UserIDs, info.MXID) { + content.Mentions.UserIDs = append(content.Mentions.UserIDs, info.MXID) + } + mentionLink = info.MXID.URI().MatrixToURL() + case socket.MentionTypeThread: + // TODO: how does one send thread mentions? + } + if mentionLink == "" { + continue + } + + output.WriteString(utf16Text[prevEnd:mention.Offset].String() + `` + utf16Text[mention.Offset:end].String() + ``) + prevEnd = end } - output.WriteString(utf16Text[prevEnd:mention.Offset].String()) - output.WriteString(``) - output.WriteString(utf16Text[mention.Offset:end].String()) - output.WriteString(``) - prevEnd = end + output.WriteString(utf16Text[prevEnd:].String()) + + outputString = output.String() } - output.WriteString(utf16Text[prevEnd:].String()) + + // Second parsing pass, replacing other formatting: + outputString = META_BOLD_REGEX.ReplaceAllString(outputString, "$1") + outputString = META_ITALIC_REGEX.ReplaceAllString(outputString, "$1") + outputString = META_STRIKE_REGEX.ReplaceAllString(outputString, "$1") + outputString = META_MONOSPACE_REGEX.ReplaceAllString(outputString, "$1") + outputString = META_MONOSPACE_BLOCK_REGEX.ReplaceAllString(outputString, "
$1
") + content.Format = event.FormatHTML - content.FormattedBody = output.String() + content.FormattedBody = outputString log := zerolog.Ctx(ctx) log.Debug().Str("text", text).Str("formatted_body", content.FormattedBody).Msg("Converted message to Matrix text")