Browse Source

Add Matrix->Discord mention bridging

Tulir Asokan 3 years ago
parent
commit
c980634783
2 changed files with 66 additions and 9 deletions
  1. 59 3
      formatter.go
  2. 7 6
      portal.go

+ 59 - 3
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)
 	}

+ 7 - 6
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 {