forked from MirrorHub/mautrix-whatsapp
Add support for intentional mentions in outgoing messages
This commit is contained in:
parent
7aa0cc1b7a
commit
10aa66a128
3 changed files with 73 additions and 19 deletions
|
@ -1,3 +1,7 @@
|
|||
# v0.8.6 (unreleased)
|
||||
|
||||
* Implemented intentional mentions for outgoing messages.
|
||||
|
||||
# v0.8.5 (2023-05-16)
|
||||
|
||||
* Added option to disable reply fallbacks entirely.
|
||||
|
|
|
@ -20,10 +20,11 @@ import (
|
|||
"fmt"
|
||||
"html"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/format"
|
||||
"maunium.net/go/mautrix/id"
|
||||
|
@ -36,7 +37,7 @@ var codeBlockRegex = regexp.MustCompile("```(?:.|\n)+?```")
|
|||
var inlineURLRegex = regexp.MustCompile(`\[(.+?)]\((.+?)\)`)
|
||||
|
||||
const mentionedJIDsContextKey = "fi.mau.whatsapp.mentioned_jids"
|
||||
const disableMentionsContextKey = "fi.mau.whatsapp.no_mentions"
|
||||
const allowedMentionsContextKey = "fi.mau.whatsapp.allowed_mentions"
|
||||
|
||||
type Formatter struct {
|
||||
bridge *WABridge
|
||||
|
@ -56,17 +57,24 @@ func NewFormatter(bridge *WABridge) *Formatter {
|
|||
Newline: "\n",
|
||||
|
||||
PillConverter: func(displayname, mxid, eventID string, ctx format.Context) string {
|
||||
_, disableMentions := ctx.ReturnData[disableMentionsContextKey]
|
||||
if mxid[0] == '@' && !disableMentions {
|
||||
puppet := bridge.GetPuppetByMXID(id.UserID(mxid))
|
||||
if puppet != nil {
|
||||
allowedMentions, _ := ctx.ReturnData[allowedMentionsContextKey].(map[types.JID]bool)
|
||||
if mxid[0] == '@' {
|
||||
var jid types.JID
|
||||
if puppet := bridge.GetPuppetByMXID(id.UserID(mxid)); puppet != nil {
|
||||
jid = puppet.JID
|
||||
} else if user := bridge.GetUserByMXIDIfExists(id.UserID(mxid)); user != nil {
|
||||
jid = user.JID.ToNonAD()
|
||||
}
|
||||
if !jid.IsEmpty() && (allowedMentions == nil || allowedMentions[jid]) {
|
||||
if allowedMentions == nil {
|
||||
jids, ok := ctx.ReturnData[mentionedJIDsContextKey].([]string)
|
||||
if !ok {
|
||||
ctx.ReturnData[mentionedJIDsContextKey] = []string{puppet.JID.String()}
|
||||
ctx.ReturnData[mentionedJIDsContextKey] = []string{jid.String()}
|
||||
} else {
|
||||
ctx.ReturnData[mentionedJIDsContextKey] = append(jids, puppet.JID.String())
|
||||
ctx.ReturnData[mentionedJIDsContextKey] = append(jids, jid.String())
|
||||
}
|
||||
return "@" + puppet.JID.User
|
||||
}
|
||||
return "@" + jid.User
|
||||
}
|
||||
}
|
||||
return displayname
|
||||
|
@ -143,7 +151,6 @@ func (formatter *Formatter) ParseWhatsApp(roomID id.RoomID, content *event.Messa
|
|||
content.Mentions.UserIDs = append(content.Mentions.UserIDs, mxid)
|
||||
}
|
||||
}
|
||||
content.UnstableMentions = content.Mentions
|
||||
if output != content.Body || forceHTML {
|
||||
output = strings.ReplaceAll(output, "\n", "<br/>")
|
||||
content.FormattedBody = output
|
||||
|
@ -154,15 +161,38 @@ func (formatter *Formatter) ParseWhatsApp(roomID id.RoomID, content *event.Messa
|
|||
}
|
||||
}
|
||||
|
||||
func (formatter *Formatter) ParseMatrix(html string) (string, []string) {
|
||||
func (formatter *Formatter) ParseMatrix(html string, mentions *event.Mentions) (string, []string) {
|
||||
ctx := format.NewContext()
|
||||
var mentionedJIDs []string
|
||||
if mentions != nil {
|
||||
var allowedMentions = make(map[types.JID]bool)
|
||||
mentionedJIDs = make([]string, 0, len(mentions.UserIDs))
|
||||
for _, userID := range mentions.UserIDs {
|
||||
var jid types.JID
|
||||
if puppet := formatter.bridge.GetPuppetByMXID(userID); puppet != nil {
|
||||
jid = puppet.JID
|
||||
mentionedJIDs = append(mentionedJIDs, puppet.JID.String())
|
||||
} else if user := formatter.bridge.GetUserByMXIDIfExists(userID); user != nil {
|
||||
jid = user.JID.ToNonAD()
|
||||
}
|
||||
if !jid.IsEmpty() && !allowedMentions[jid] {
|
||||
allowedMentions[jid] = true
|
||||
mentionedJIDs = append(mentionedJIDs, jid.String())
|
||||
}
|
||||
}
|
||||
ctx.ReturnData[allowedMentionsContextKey] = allowedMentions
|
||||
}
|
||||
result := formatter.matrixHTMLParser.Parse(html, ctx)
|
||||
mentionedJIDs, _ := ctx.ReturnData[mentionedJIDsContextKey].([]string)
|
||||
if mentions == nil {
|
||||
mentionedJIDs, _ = ctx.ReturnData[mentionedJIDsContextKey].([]string)
|
||||
sort.Strings(mentionedJIDs)
|
||||
mentionedJIDs = slices.Compact(mentionedJIDs)
|
||||
}
|
||||
return result, mentionedJIDs
|
||||
}
|
||||
|
||||
func (formatter *Formatter) ParseMatrixWithoutMentions(html string) string {
|
||||
ctx := format.NewContext()
|
||||
ctx.ReturnData[disableMentionsContextKey] = true
|
||||
ctx.ReturnData[allowedMentionsContextKey] = map[types.JID]struct{}{}
|
||||
return formatter.matrixHTMLParser.Parse(html, ctx)
|
||||
}
|
||||
|
|
26
portal.go
26
portal.go
|
@ -43,6 +43,7 @@ import (
|
|||
|
||||
"github.com/chai2010/webp"
|
||||
"github.com/tidwall/gjson"
|
||||
"golang.org/x/exp/slices"
|
||||
"golang.org/x/image/draw"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
|
@ -1880,6 +1881,22 @@ func (portal *Portal) MainIntent() *appservice.IntentAPI {
|
|||
return portal.bridge.Bot
|
||||
}
|
||||
|
||||
func (portal *Portal) addReplyMention(content *event.MessageEventContent, sender types.JID) {
|
||||
if content.Mentions == nil {
|
||||
return
|
||||
}
|
||||
var mxid id.UserID
|
||||
if user := portal.bridge.GetUserByJID(sender); user != nil {
|
||||
mxid = user.MXID
|
||||
} else {
|
||||
puppet := portal.bridge.GetPuppetByJID(sender)
|
||||
mxid = puppet.MXID
|
||||
}
|
||||
if slices.Contains(content.Mentions.UserIDs, mxid) {
|
||||
content.Mentions.UserIDs = append(content.Mentions.UserIDs, mxid)
|
||||
}
|
||||
}
|
||||
|
||||
func (portal *Portal) SetReply(content *event.MessageEventContent, replyTo *ReplyInfo, isBackfill bool) bool {
|
||||
if replyTo == nil {
|
||||
return false
|
||||
|
@ -1908,10 +1925,13 @@ func (portal *Portal) SetReply(content *event.MessageEventContent, replyTo *Repl
|
|||
if message == nil || message.IsFakeMXID() {
|
||||
if isBackfill && portal.bridge.Config.Homeserver.Software == bridgeconfig.SoftwareHungry {
|
||||
content.RelatesTo = (&event.RelatesTo{}).SetReplyTo(targetPortal.deterministicEventID(replyTo.Sender, replyTo.MessageID, ""))
|
||||
portal.addReplyMention(content, replyTo.Sender)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
// TODO store sender mxid in db message
|
||||
portal.addReplyMention(content, message.Sender)
|
||||
content.RelatesTo = (&event.RelatesTo{}).SetReplyTo(message.MXID)
|
||||
if portal.bridge.Config.Bridge.DisableReplyFallbacks {
|
||||
return true
|
||||
|
@ -3395,7 +3415,7 @@ func (portal *Portal) preprocessMatrixMedia(ctx context.Context, sender *User, r
|
|||
hasHTMLCaption = content.Format == event.FormatHTML
|
||||
}
|
||||
if relaybotFormatted || hasHTMLCaption {
|
||||
caption, mentionedJIDs = portal.bridge.Formatter.ParseMatrix(content.FormattedBody)
|
||||
caption, mentionedJIDs = portal.bridge.Formatter.ParseMatrix(content.FormattedBody, content.Mentions)
|
||||
}
|
||||
|
||||
var file *event.EncryptedFileInfo
|
||||
|
@ -3621,7 +3641,7 @@ func (portal *Portal) msc1767ToWhatsApp(msg MSC1767Message, mentions bool) (stri
|
|||
}
|
||||
if msg.HTML != "" {
|
||||
if mentions {
|
||||
return portal.bridge.Formatter.ParseMatrix(msg.HTML)
|
||||
return portal.bridge.Formatter.ParseMatrix(msg.HTML, nil)
|
||||
} else {
|
||||
return portal.bridge.Formatter.ParseMatrixWithoutMentions(msg.HTML), nil
|
||||
}
|
||||
|
@ -3858,7 +3878,7 @@ func (portal *Portal) convertMatrixMessage(ctx context.Context, sender *User, ev
|
|||
return nil, sender, extraMeta, errMNoticeDisabled
|
||||
}
|
||||
if content.Format == event.FormatHTML {
|
||||
text, ctxInfo.MentionedJid = portal.bridge.Formatter.ParseMatrix(content.FormattedBody)
|
||||
text, ctxInfo.MentionedJid = portal.bridge.Formatter.ParseMatrix(content.FormattedBody, content.Mentions)
|
||||
}
|
||||
if content.MsgType == event.MsgEmote && !relaybotFormatted {
|
||||
text = "/me " + text
|
||||
|
|
Loading…
Reference in a new issue