Use convert replies to embeds when sending via webhook

Fixes #68
This commit is contained in:
Tulir Asokan
2023-05-06 18:48:51 +03:00
parent 2e07cbfa0b
commit 35f534affa

View File

@@ -1305,6 +1305,61 @@ func (portal *Portal) getRelayUserMeta(sender *User) (name, avatarURL string) {
return
}
const replyEmbedMaxLines = 1
const replyEmbedMaxChars = 72
func cutBody(body string) string {
lines := strings.Split(strings.TrimSpace(body), "\n")
var output string
for i, line := range lines {
if i >= replyEmbedMaxLines {
output += " […]"
break
}
if i > 0 {
output += "\n"
}
output += line
if len(output) > replyEmbedMaxChars {
output = output[:replyEmbedMaxChars] + "…"
break
}
}
return output
}
func (portal *Portal) convertReplyMessageToEmbed(eventID id.EventID, url string) (*discordgo.MessageEmbed, error) {
evt, err := portal.MainIntent().GetEvent(portal.MXID, eventID)
if err != nil {
return nil, fmt.Errorf("failed to fetch event: %w", err)
}
err = evt.Content.ParseRaw(evt.Type)
if err != nil {
return nil, fmt.Errorf("failed to parse event content: %w", err)
}
content, ok := evt.Content.Parsed.(*event.MessageEventContent)
if !ok {
return nil, fmt.Errorf("unsupported event type %s / %T", evt.Type.String(), evt.Content.Parsed)
}
content.RemoveReplyFallback()
var targetUser string
puppet := portal.bridge.GetPuppetByMXID(evt.Sender)
if puppet != nil {
targetUser = fmt.Sprintf("<@%s>", puppet.ID)
} else if user := portal.bridge.GetUserByMXID(evt.Sender); user != nil && user.DiscordID != "" {
targetUser = fmt.Sprintf("<@%s>", user.DiscordID)
} else if member := portal.bridge.StateStore.GetMember(portal.MXID, evt.Sender); member != nil && member.Displayname != "" {
targetUser = member.Displayname
} else {
targetUser = evt.Sender.String()
}
body := escapeDiscordMarkdown(cutBody(content.Body))
body = fmt.Sprintf("**[Replying to](%s) %s**\n%s", url, targetUser, body)
embed := &discordgo.MessageEmbed{Description: body}
return embed, nil
}
func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
if portal.IsPrivateChat() && sender.DiscordID != portal.Key.Receiver {
go portal.sendMessageMetrics(evt, errUserNotReceiver, "Ignoring")
@@ -1323,6 +1378,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
go portal.sendMessageMetrics(evt, errUserNotLoggedIn, "Ignoring")
return
}
isWebhookSend := sess == nil
var threadID string
if editMXID := content.GetRelatesTo().GetReplaceID(); editMXID != "" && content.NewContent != nil {
@@ -1330,7 +1386,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
if edits != nil {
discordContent, allowedMentions := portal.parseMatrixHTML(content.NewContent)
var err error
if sess != nil {
if !isWebhookSend {
// TODO save edit in message table
_, err = sess.ChannelMessageEdit(edits.DiscordProtoChannelID(), edits.DiscordID, discordContent)
} else {
@@ -1349,7 +1405,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
if existingThread != nil {
threadID = existingThread.ID
} else {
if sess == nil {
if isWebhookSend {
// TODO start thread with bot?
go portal.sendMessageMetrics(evt, errCantStartThread, "Dropping")
return
@@ -1378,17 +1434,27 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
}
}
switch content.MsgType {
case event.MsgText, event.MsgEmote, event.MsgNotice:
if replyToMXID := content.RelatesTo.GetNonFallbackReplyTo(); replyToMXID != "" {
replyTo := portal.bridge.DB.Message.GetByMXID(portal.Key, replyToMXID)
if replyTo != nil && replyTo.ThreadID == threadID {
if replyToMXID := content.RelatesTo.GetNonFallbackReplyTo(); replyToMXID != "" {
replyTo := portal.bridge.DB.Message.GetByMXID(portal.Key, replyToMXID)
if replyTo != nil && replyTo.ThreadID == threadID {
if isWebhookSend {
messageURL := fmt.Sprintf("https://discord.com/channels/%s/%s/%s", portal.GuildID, channelID, replyTo.DiscordID)
embed, err := portal.convertReplyMessageToEmbed(replyTo.MXID, messageURL)
if err != nil {
portal.log.Warn().Err(err).Msg("Failed to convert reply message to embed for webhook send")
} else if embed != nil {
sendReq.Embeds = []*discordgo.MessageEmbed{embed}
}
} else {
sendReq.Reference = &discordgo.MessageReference{
ChannelID: channelID,
MessageID: replyTo.DiscordID,
}
}
}
}
switch content.MsgType {
case event.MsgText, event.MsgEmote, event.MsgNotice:
sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content)
case event.MsgAudio, event.MsgFile, event.MsgImage, event.MsgVideo:
data, err := downloadMatrixAttachment(portal.MainIntent(), content)
@@ -1402,7 +1468,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
sendReq.Content, sendReq.AllowedMentions = portal.parseMatrixHTML(content)
}
if sess != nil && sess.IsUser {
if !isWebhookSend && sess.IsUser {
att := &discordgo.MessageAttachment{
ID: "0",
Filename: filename,
@@ -1438,7 +1504,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
go portal.sendMessageMetrics(evt, fmt.Errorf("%w %q", errUnknownMsgType, content.MsgType), "Ignoring")
return
}
if sess != nil {
if !isWebhookSend {
// AllowedMentions must not be set for real users, and it's also not that useful for personal bots.
// It's only important for relaying, where the webhook may have higher permissions than the user on Matrix.
sendReq.AllowedMentions = nil
@@ -1455,7 +1521,7 @@ func (portal *Portal) handleMatrixMessage(sender *User, evt *event.Event) {
sendReq.Nonce = generateNonce()
var msg *discordgo.Message
var err error
if sess != nil {
if !isWebhookSend {
msg, err = sess.ChannelMessageSendComplex(channelID, &sendReq)
} else {
username, avatarURL := portal.getRelayUserMeta(sender)