diff --git a/formatter.go b/formatter.go index b59ef5d..934ac07 100644 --- a/formatter.go +++ b/formatter.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/yuin/goldmark" + "maunium.net/go/mautrix/id" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/format" @@ -23,8 +24,55 @@ func renderDiscordMarkdown(text string) event.MessageEventContent { return format.RenderMarkdownCustom(text, mdRenderer) } +const formatterContextUserKey = "fi.mau.discord.user" +const formatterContextPortalKey = "fi.mau.discord.portal" + +func pillConverter(displayname, mxid, eventID string, ctx format.Context) string { + if len(mxid) == 0 { + return displayname + } + user := ctx[formatterContextUserKey].(*User) + if mxid[0] == '#' { + alias, err := user.bridge.Bot.ResolveAlias(id.RoomAlias(mxid)) + if err != nil { + return displayname + } + mxid = alias.RoomID.String() + } + if mxid[0] == '!' { + portal := user.bridge.GetPortalByMXID(id.RoomID(mxid)) + if portal != nil { + if eventID == "" { + //currentPortal := ctx[formatterContextPortalKey].(*Portal) + return fmt.Sprintf("<#%s>", portal.Key.ChannelID) + //if currentPortal.GuildID == portal.GuildID { + //} else if portal.GuildID != "" { + // return fmt.Sprintf("<#%s:%s:%s>", portal.Key.ChannelID, portal.GuildID, portal.Name) + //} else { + // // TODO is mentioning private channels possible at all? + //} + } else if msg := user.bridge.DB.Message.GetByMXID(portal.Key, id.EventID(eventID)); msg != nil { + guildID := portal.GuildID + if guildID == "" { + guildID = "@me" + } + return fmt.Sprintf("https://discord.com/channels/%s/%s/%s", guildID, msg.DiscordProtoChannelID(), msg.DiscordID) + } + } + } else if mxid[0] == '@' { + parsedID, ok := user.bridge.ParsePuppetMXID(id.UserID(mxid)) + if ok { + return fmt.Sprintf("<@%s>", parsedID) + } + mentionedUser := user.bridge.GetUserByMXID(id.UserID(mxid)) + if mentionedUser != nil && mentionedUser.DiscordID != "" { + return fmt.Sprintf("<@%s>", mentionedUser.DiscordID) + } + } + return displayname +} + var matrixHTMLParser = &format.HTMLParser{ - PillConverter: nil, TabsToSpaces: 4, Newline: "\n", HorizontalLine: "\n---\n", @@ -45,6 +93,10 @@ var matrixHTMLParser = &format.HTMLParser{ }, } +func init() { + matrixHTMLParser.PillConverter = pillConverter +} + var discordMarkdownEscaper = strings.NewReplacer( `\`, `\\`, `_`, `\_`, @@ -52,11 +104,15 @@ var discordMarkdownEscaper = strings.NewReplacer( `~`, `\~`, "`", "\\`", `|`, `\|`, + `<`, `\<`, ) -func parseMatrixHTML(content *event.MessageEventContent) string { +func (portal *Portal) parseMatrixHTML(user *User, content *event.MessageEventContent) string { if content.Format == event.FormatHTML && len(content.FormattedBody) > 0 { - return matrixHTMLParser.Parse(content.FormattedBody, make(format.Context)) + return matrixHTMLParser.Parse(content.FormattedBody, format.Context{ + formatterContextUserKey: user, + formatterContextPortalKey: portal, + }) } else { return discordMarkdownEscaper.Replace(content.Body) } diff --git a/portal.go b/portal.go index a2e316e..6874ccb 100644 --- a/portal.go +++ b/portal.go @@ -929,9 +929,10 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) { if editMXID := content.GetRelatesTo().GetReplaceID(); editMXID != "" && content.NewContent != nil { edits := portal.bridge.DB.Message.GetByMXID(portal.Key, editMXID) if edits != nil { + discordContent := portal.parseMatrixHTML(sender, content.NewContent) // we don't have anything to save for the update message right now // as we're not tracking edited timestamps. - _, err := sender.Session.ChannelMessageEdit(edits.DiscordProtoChannelID(), edits.DiscordID, parseMatrixHTML(content.NewContent)) + _, err := sender.Session.ChannelMessageEdit(edits.DiscordProtoChannelID(), edits.DiscordID, discordContent) if err != nil { portal.log.Errorln("Failed to update message %s: %v", edits.DiscordID, err) } @@ -966,7 +967,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) { } } } - sendReq.Content = parseMatrixHTML(content) + sendReq.Content = portal.parseMatrixHTML(sender, content) case event.MsgAudio, event.MsgFile, event.MsgImage, event.MsgVideo: data, err := portal.downloadMatrixAttachment(evt.ID, content) if err != nil { @@ -1289,15 +1290,15 @@ func (portal *Portal) handleDiscordReaction(user *User, reaction *discordgo.Mess } } -func (portal *Portal) handleMatrixRedaction(user *User, evt *event.Event) { - if user.DiscordID != portal.Key.Receiver { +func (portal *Portal) handleMatrixRedaction(sender *User, evt *event.Event) { + if portal.IsPrivateChat() && sender.DiscordID != portal.Key.Receiver { return } // First look if we're redacting a message message := portal.bridge.DB.Message.GetByMXID(portal.Key, evt.Redacts) if message != nil { - err := user.Session.ChannelMessageDelete(message.DiscordProtoChannelID(), message.DiscordID) + err := sender.Session.ChannelMessageDelete(message.DiscordProtoChannelID(), message.DiscordID) if err != nil { portal.log.Debugfln("Failed to delete discord message %s: %v", message.DiscordID, err) } else { @@ -1309,7 +1310,7 @@ func (portal *Portal) handleMatrixRedaction(user *User, evt *event.Event) { // Now check if it's a reaction. reaction := portal.bridge.DB.Reaction.GetByMXID(evt.Redacts) if reaction != nil && reaction.Channel == portal.Key { - err := user.Session.MessageReactionRemove(reaction.DiscordProtoChannelID(), reaction.MessageID, reaction.EmojiName, reaction.Sender) + err := sender.Session.MessageReactionRemove(reaction.DiscordProtoChannelID(), reaction.MessageID, reaction.EmojiName, reaction.Sender) if err != nil { portal.log.Debugfln("Failed to delete reaction %s from %s: %v", reaction.EmojiName, reaction.MessageID, err) } else {