From e2b0879f163235c4a735a828654c5b90a96b7e18 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Mon, 3 Jan 2022 16:11:39 +0200 Subject: [PATCH] Add support for multi-contact messages --- CHANGELOG.md | 1 + historysync.go | 11 +++++++++++ portal.go | 42 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3fa3872..d76452f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Added overrides for mime type -> file extension mapping as some OSes have very obscure extensions in their mime type database. * Added support for personal filtering spaces (started by [@HelderFSFerreira] and [@clmnin] in [#413]). +* Added support for multi-contact messages. * Fixed avatar remove events from WhatsApp being ignored. * Fixed the bridge using the wrong Olm session if a client established a new one due to corruption. diff --git a/historysync.go b/historysync.go index d7408ac..5a65384 100644 --- a/historysync.go +++ b/historysync.go @@ -23,6 +23,7 @@ import ( waProto "go.mau.fi/whatsmeow/binary/proto" "go.mau.fi/whatsmeow/types" + "maunium.net/go/mautrix" "maunium.net/go/mautrix/appservice" "maunium.net/go/mautrix/event" @@ -509,6 +510,16 @@ func (portal *Portal) appendBatchEvents(converted *ConvertedMessage, info *types *eventsArray = append(*eventsArray, mainEvt) *infoArray = append(*infoArray, info) } + if converted.MultiEvent != nil { + for _, subEvtContent := range converted.MultiEvent { + subEvt, err := portal.wrapBatchEvent(info, converted.Intent, converted.Type, subEvtContent, nil) + if err != nil { + return err + } + *eventsArray = append(*eventsArray, subEvt) + *infoArray = append(*infoArray, nil) + } + } return nil } diff --git a/portal.go b/portal.go index 1a92038..6df9020 100644 --- a/portal.go +++ b/portal.go @@ -267,7 +267,7 @@ func containsSupportedMessage(waMsg *waProto.Message) bool { return waMsg.Conversation != nil || waMsg.ExtendedTextMessage != nil || waMsg.ImageMessage != nil || waMsg.StickerMessage != nil || waMsg.AudioMessage != nil || waMsg.VideoMessage != nil || waMsg.DocumentMessage != nil || waMsg.ContactMessage != nil || waMsg.LocationMessage != nil || - waMsg.LiveLocationMessage != nil || waMsg.GroupInviteMessage != nil + waMsg.LiveLocationMessage != nil || waMsg.GroupInviteMessage != nil || waMsg.ContactsArrayMessage != nil } func isPotentiallyInteresting(waMsg *waProto.Message) bool { @@ -276,7 +276,7 @@ func isPotentiallyInteresting(waMsg *waProto.Message) bool { } // List of message types that aren't supported, but might potentially be interesting // (so a warning should be logged if they are encountered). - return waMsg.Call != nil || waMsg.Chat != nil || waMsg.ContactsArrayMessage != nil || + return waMsg.Call != nil || waMsg.Chat != nil || waMsg.HighlyStructuredMessage != nil || waMsg.SendPaymentMessage != nil || waMsg.LiveLocationMessage != nil || waMsg.RequestPaymentMessage != nil || waMsg.DeclinePaymentRequestMessage != nil || waMsg.CancelPaymentRequestMessage != nil || waMsg.TemplateMessage != nil || @@ -304,6 +304,8 @@ func getMessageType(waMsg *waProto.Message) string { return fmt.Sprintf("document %s", waMsg.GetDocumentMessage().GetMimetype()) case waMsg.ContactMessage != nil: return "contact" + case waMsg.ContactsArrayMessage != nil: + return "contact array" case waMsg.LocationMessage != nil: return "location" case waMsg.LiveLocationMessage != nil: @@ -342,6 +344,8 @@ func (portal *Portal) convertMessage(intent *appservice.IntentAPI, source *User, return portal.convertMediaMessage(intent, source, info, waMsg.GetDocumentMessage()) case waMsg.ContactMessage != nil: return portal.convertContactMessage(intent, waMsg.GetContactMessage()) + case waMsg.ContactsArrayMessage != nil: + return portal.convertContactsArrayMessage(intent, waMsg.GetContactsArrayMessage()) case waMsg.LocationMessage != nil: return portal.convertLocationMessage(intent, waMsg.GetLocationMessage()) case waMsg.LiveLocationMessage != nil: @@ -478,6 +482,14 @@ func (portal *Portal) handleMessage(source *User, evt *events.Message) { eventID = resp.EventID } } + if converted.MultiEvent != nil && existingMsg == nil { + for index, subEvt := range converted.MultiEvent { + resp, err = portal.sendMessage(converted.Intent, converted.Type, subEvt, nil, evt.Info.Timestamp.UnixMilli()) + if err != nil { + portal.log.Errorfln("Failed to send sub-event %d of %s to Matrix: %v", index+1, msgID, err) + } + } + } if len(eventID) != 0 { portal.finishHandling(existingMsg, &evt.Info, resp.EventID, false) } @@ -1302,6 +1314,8 @@ type ConvertedMessage struct { Extra map[string]interface{} Caption *event.MessageEventContent + MultiEvent []*event.MessageEventContent + ReplyTo types.MessageID } @@ -1455,6 +1469,30 @@ func (portal *Portal) convertContactMessage(intent *appservice.IntentAPI, msg *w } } +func (portal *Portal) convertContactsArrayMessage(intent *appservice.IntentAPI, msg *waProto.ContactsArrayMessage) *ConvertedMessage { + name := msg.GetDisplayName() + if len(name) == 0 { + name = fmt.Sprintf("%d contacts", len(msg.GetContacts())) + } + contacts := make([]*event.MessageEventContent, 0, len(msg.GetContacts())) + for _, contact := range msg.GetContacts() { + converted := portal.convertContactMessage(intent, contact) + if converted != nil { + contacts = append(contacts, converted.Content) + } + } + return &ConvertedMessage{ + Intent: intent, + Type: event.EventMessage, + Content: &event.MessageEventContent{ + MsgType: event.MsgNotice, + Body: fmt.Sprintf("Sent %s", name), + }, + ReplyTo: msg.GetContextInfo().GetStanzaId(), + MultiEvent: contacts, + } +} + func (portal *Portal) tryKickUser(userID id.UserID, intent *appservice.IntentAPI) error { _, err := intent.KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: userID}) if err != nil {