From 1e0384d6a80752b71e9800eb8464211d297cb8c1 Mon Sep 17 00:00:00 2001 From: chad <0xjasperstone@gmail.com> Date: Sun, 2 Aug 2026 14:32:05 +0000 Subject: [PATCH] msgconv: preserve user group mentions --- pkg/msgconv/blocks.go | 4 +++ pkg/msgconv/blocks_test.go | 21 +++++++++++++ pkg/msgconv/mrkdwn/tag.go | 15 ++++++++-- pkg/msgconv/mrkdwn/tag_test.go | 55 ++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 2 deletions(-) create mode 100644 pkg/msgconv/mrkdwn/tag_test.go diff --git a/pkg/msgconv/blocks.go b/pkg/msgconv/blocks.go index 84e3c4a..8d96e27 100644 --- a/pkg/msgconv/blocks.go +++ b/pkg/msgconv/blocks.go @@ -299,6 +299,10 @@ func (mc *MessageConverter) renderRichTextSectionElements( openingTags(&htmlText, e.Style) mrkdwn.RoomMentionToHTML(&htmlText, e.ChannelID, mxid, alias, name, mc.ServerName) closingTags(&htmlText, e.Style) + case *slack.RichTextSectionUserGroupElement: + openingTags(&htmlText, e.Style) + mrkdwn.UserGroupMentionToHTML(&htmlText, e.UsergroupID, "") + closingTags(&htmlText, e.Style) case *slack.RichTextSectionLinkElement: var linkText string if e.Text != "" { diff --git a/pkg/msgconv/blocks_test.go b/pkg/msgconv/blocks_test.go index d1f5cd4..a1ba8a9 100644 --- a/pkg/msgconv/blocks_test.go +++ b/pkg/msgconv/blocks_test.go @@ -86,6 +86,27 @@ func TestSlackBlocksToMatrixMessageMention(t *testing.T) { } } +func TestSlackBlocksToMatrixUserGroupMention(t *testing.T) { + mc := testMessageConverter() + part, err := mc.slackBlocksToMatrix(context.Background(), nil, nil, slack.Blocks{ + BlockSet: []slack.Block{ + slack.NewRichTextBlock("", slack.NewRichTextSection( + slack.NewRichTextSectionTextElement("Hi ", nil), + slack.NewRichTextSectionUserGroupElement("S123"), + )), + }, + }, nil) + if err != nil { + t.Fatal(err) + } + if part.Content.Body != "Hi " { + t.Fatalf("unexpected body: %q", part.Content.Body) + } + if part.Content.FormattedBody != "Hi <!subteam^S123>" { + t.Fatalf("unexpected formatted body: %q", part.Content.FormattedBody) + } +} + func TestSlackBlocksToMatrixMessageMentionPermalink(t *testing.T) { mc := testMessageConverter() part, err := mc.slackBlocksToMatrix(context.Background(), nil, nil, slack.Blocks{ diff --git a/pkg/msgconv/mrkdwn/tag.go b/pkg/msgconv/mrkdwn/tag.go index 80e310a..131f0da 100644 --- a/pkg/msgconv/mrkdwn/tag.go +++ b/pkg/msgconv/mrkdwn/tag.go @@ -166,7 +166,6 @@ func (s *slackTagParser) Parse(parent ast.Node, block text.Reader, pc parser.Con switch content { case "channel", "everyone", "here": pc.Get(ContextKeyMentions).(*event.Mentions).Room = true - default: } return &astSlackSpecialMention{astSlackTag: tag, content: content} case "": @@ -208,6 +207,16 @@ func RoomMentionToHTML(out io.Writer, channelID string, mxid id.RoomID, alias id } } +func UserGroupMentionToHTML(out io.Writer, userGroupID, name string) { + if name == "" { + _, _ = fmt.Fprintf(out, "<!subteam^%s>", html.EscapeString(userGroupID)) + } else if strings.HasPrefix(name, "@") { + _, _ = io.WriteString(out, html.EscapeString(name)) + } else { + _, _ = fmt.Fprintf(out, "@%s", html.EscapeString(name)) + } +} + func (r *slackTagHTMLRenderer) renderSlackTag(w goldmarkUtil.BufWriter, source []byte, n ast.Node, entering bool) (status ast.WalkStatus, err error) { status = ast.WalkContinue if !entering { @@ -256,7 +265,9 @@ func (r *slackTagHTMLRenderer) renderSlackTag(w goldmarkUtil.BufWriter, source [ // do @room mentions? return case "subteam": - // do subteam handling? more spaces? + if len(parts) > 1 { + UserGroupMentionToHTML(w, parts[1], node.label) + } return default: return diff --git a/pkg/msgconv/mrkdwn/tag_test.go b/pkg/msgconv/mrkdwn/tag_test.go new file mode 100644 index 0000000..8ea919d --- /dev/null +++ b/pkg/msgconv/mrkdwn/tag_test.go @@ -0,0 +1,55 @@ +// mautrix-slack - A Matrix-Slack puppeting bridge. +// Copyright (C) 2024 Tulir Asokan +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package mrkdwn + +import ( + "context" + "testing" + + "maunium.net/go/mautrix/event" +) + +func TestUserGroupMention(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "embedded label", + input: "Hi ", + expected: "Hi @platform-team", + }, + { + name: "raw fallback", + input: "Hi ", + expected: "Hi <!subteam^S404>", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + parser := New(&Params{}) + output, err := parser.Parse(context.Background(), test.input, &event.Mentions{}) + if err != nil { + t.Fatal(err) + } + if output != test.expected { + t.Fatalf("unexpected output: %q", output) + } + }) + } +}