1
0
Fork 0

feat: bridge XMPP mentions as proper whatsapp mentions

This has outgoing mentions of participant usernames in MUCs be correctly
represented as mentions in official WhatsApp clients.
This commit is contained in:
nicoco 2024-02-10 07:10:42 +01:00
parent c40c9747e4
commit 6727da086a
5 changed files with 47 additions and 17 deletions

View File

@ -148,6 +148,7 @@ type Message struct {
ReplyBody string // The full body of the message this message is in reply to, if any. ReplyBody string // The full body of the message this message is in reply to, if any.
Attachments []Attachment // The list of file (image, video, etc.) attachments contained in this message. Attachments []Attachment // The list of file (image, video, etc.) attachments contained in this message.
Preview Preview // A short description for the URL provided in the message body, if any. Preview Preview // A short description for the URL provided in the message body, if any.
MentionJIDs []string // A list of JIDs mentioned in this message, if any.
} }
// A Attachment represents additional binary data (e.g. images, videos, documents) provided alongside // A Attachment represents additional binary data (e.g. images, videos, documents) provided alongside

View File

@ -3,6 +3,7 @@ from datetime import datetime, timezone
from typing import TYPE_CHECKING, Optional from typing import TYPE_CHECKING, Optional
from slidge.group import LegacyBookmarks, LegacyMUC, LegacyParticipant, MucType from slidge.group import LegacyBookmarks, LegacyMUC, LegacyParticipant, MucType
from slidge.util.types import Mention
from slixmpp.exceptions import XMPPError from slixmpp.exceptions import XMPPError
from .generated import whatsapp from .generated import whatsapp
@ -96,7 +97,7 @@ class MUC(LegacyMUC[str, str, Participant, str]):
participant.role = "moderator" participant.role = "moderator"
def replace_mentions(self, t: str): def replace_mentions(self, t: str):
return replace_mentions( return replace_whatsapp_mentions(
t, t,
participants=( participants=(
{ {
@ -168,10 +169,19 @@ class Bookmarks(LegacyBookmarks[str, MUC]):
return whatsapp_group_id return whatsapp_group_id
def replace_mentions(t: str, participants: dict[str, str]): def replace_xmpp_mentions(text: str, mentions: list[Mention]):
def match(m: re.Match): offset: int = 0
mat = m.group(0) result: str = ""
sub = participants.get(mat.replace("@", "+"), mat) for m in mentions:
return sub legacy_id = "@" + m.contact.legacy_id[: m.contact.legacy_id.find("@")]
result = result + text[offset : m.start] + legacy_id
offset = m.end
return result + text[offset:] if offset > 0 else text
return re.sub(r"@\d+", match, t)
def replace_whatsapp_mentions(text: str, participants: dict[str, str]):
def match(m: re.Match):
group = m.group(0)
return participants.get(group.replace("@", "+"), group)
return re.sub(r"@\d+", match, text)

View File

@ -303,6 +303,17 @@ func (s *Session) getMessagePayload(message Message) *proto.Message {
} }
} }
// Attach any inline mentions extended metadata.
if len(message.MentionJIDs) > 0 {
if payload == nil {
payload = &proto.Message{ExtendedTextMessage: &proto.ExtendedTextMessage{Text: &message.Body}}
}
if payload.ExtendedTextMessage.ContextInfo == nil {
payload.ExtendedTextMessage.ContextInfo = &proto.ContextInfo{}
}
payload.ExtendedTextMessage.ContextInfo.MentionedJid = message.MentionJIDs
}
if payload == nil { if payload == nil {
payload = &proto.Message{Conversation: &message.Body} payload = &proto.Message{Conversation: &message.Body}
} }

View File

@ -17,6 +17,7 @@ from slidge.contact.roster import ContactIsUser
from slidge.util import is_valid_phone_number from slidge.util import is_valid_phone_number
from slidge.util.types import ( from slidge.util.types import (
LegacyAttachment, LegacyAttachment,
Mention,
MessageReference, MessageReference,
PseudoPresenceShow, PseudoPresenceShow,
ResourceDict, ResourceDict,
@ -26,7 +27,7 @@ from . import config
from .contact import Contact, Roster from .contact import Contact, Roster
from .gateway import Gateway from .gateway import Gateway
from .generated import go, whatsapp from .generated import go, whatsapp
from .group import MUC, Bookmarks from .group import MUC, Bookmarks, replace_xmpp_mentions
from .util import get_bytes_temp from .util import get_bytes_temp
MESSAGE_PAIR_SUCCESS = ( MESSAGE_PAIR_SUCCESS = (
@ -244,7 +245,7 @@ class Session(BaseSession[str, Recipient]):
reply_to_msg_id: Optional[str] = None, reply_to_msg_id: Optional[str] = None,
reply_to_fallback_text: Optional[str] = None, reply_to_fallback_text: Optional[str] = None,
reply_to=None, reply_to=None,
mentions=None, mentions: Optional[list[Mention]] = None,
**_, **_,
): ):
""" """
@ -253,7 +254,11 @@ class Session(BaseSession[str, Recipient]):
message_id = self.whatsapp.GenerateMessageID() message_id = self.whatsapp.GenerateMessageID()
message_preview = await self.__get_preview(text) or whatsapp.Preview() message_preview = await self.__get_preview(text) or whatsapp.Preview()
message = whatsapp.Message( message = whatsapp.Message(
ID=message_id, JID=chat.legacy_id, Body=text, Preview=message_preview ID=message_id,
JID=chat.legacy_id,
Body=replace_xmpp_mentions(text, mentions) if mentions else text,
Preview=message_preview,
MentionJIDs=go.Slice_string([m.contact.legacy_id for m in mentions or []]),
) )
set_reply_to(chat, message, reply_to_msg_id, reply_to_fallback_text, reply_to) set_reply_to(chat, message, reply_to_msg_id, reply_to_fallback_text, reply_to)
self.whatsapp.SendMessage(message) self.whatsapp.SendMessage(message)

View File

@ -1,26 +1,29 @@
from slidge_whatsapp.group import replace_mentions from slidge_whatsapp.group import replace_whatsapp_mentions
def test_replace_mentions(): def test_replace_whatsapp_mentions():
text = "Hayo @1234, it's cool in here in with @5678!! @123333" text = "Hayo @1234, it's cool in here in with @5678!! @123333"
assert ( assert (
replace_mentions( replace_whatsapp_mentions(
text, text,
{"+1234": "bibi", "+5678": "baba"}, {"+1234": "bibi", "+5678": "baba"},
) )
== "Hayo bibi, it's cool in here in with baba!! @123333" == "Hayo bibi, it's cool in here in with baba!! @123333"
) )
assert replace_mentions(text, {}) == text assert replace_whatsapp_mentions(text, {}) == text
assert replace_mentions(text, {"+123333": "prout"}) == text.replace( assert replace_whatsapp_mentions(text, {"+123333": "prout"}) == text.replace(
"@123333", "prout" "@123333", "prout"
) )
assert replace_mentions("+1234", {"+1234": "bibi", "+5678": "baba"}) == "+1234" assert (
replace_whatsapp_mentions("+1234", {"+1234": "bibi", "+5678": "baba"})
== "+1234"
)
assert ( assert (
replace_mentions("@1234@1234@123", {"+1234": "bibi", "+5678": "baba"}) replace_whatsapp_mentions("@1234@1234@123", {"+1234": "bibi", "+5678": "baba"})
== "bibibibi@123" == "bibibibi@123"
) )