From ac2ca08007d05a35da0ed733addb80771efc178c Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 21 Feb 2021 14:45:33 +0200 Subject: [PATCH] Sync broadcast list recipients too --- custompuppet.go | 2 +- go.mod | 2 +- go.sum | 2 + portal.go | 112 ++++++++++++++++++++++++++++++------------------ 4 files changed, 75 insertions(+), 43 deletions(-) diff --git a/custompuppet.go b/custompuppet.go index 372ebb0..11475fa 100644 --- a/custompuppet.go +++ b/custompuppet.go @@ -164,7 +164,7 @@ func (puppet *Puppet) ProcessResponse(resp *mautrix.RespSync, _ string) error { } for roomID, events := range resp.Rooms.Join { portal := puppet.bridge.GetPortalByMXID(roomID) - if portal == nil { + if portal == nil || portal.IsBroadcastList() { continue } for _, evt := range events.Ephemeral.Events { diff --git a/go.mod b/go.mod index 432250a..fc2cbc3 100644 --- a/go.mod +++ b/go.mod @@ -16,4 +16,4 @@ require ( maunium.net/go/mautrix v0.8.3 ) -replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5 +replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.22-0.20210221124355-8c89650e6a9e diff --git a/go.sum b/go.sum index 7454f81..35b6793 100644 --- a/go.sum +++ b/go.sum @@ -337,6 +337,8 @@ github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94 h1:G4YvxLMW80 github.com/tulir/go-whatsapp v0.3.22-0.20210221000549-ec31478c7b94/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5 h1:4Y5xQdpuLEt4DQavhnP/Ium1zpwIE+LeOFwVyiW4qoY= github.com/tulir/go-whatsapp v0.3.22-0.20210221121735-6d3eaaa7bdc5/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= +github.com/tulir/go-whatsapp v0.3.22-0.20210221124355-8c89650e6a9e h1:jQGaSyVl7gx44KYH1UY9JnhoLAqTEKysIhLjQJZZKVY= +github.com/tulir/go-whatsapp v0.3.22-0.20210221124355-8c89650e6a9e/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= diff --git a/portal.go b/portal.go index c848ed2..6b53613 100644 --- a/portal.go +++ b/portal.go @@ -359,6 +359,43 @@ func (portal *Portal) finishHandling(source *User, message *waProto.WebMessageIn portal.log.Debugln("Handled message", message.GetKey().GetId(), "->", mxid) } +func (portal *Portal) kickExtraUsers(participantMap map[whatsapp.JID]bool) { + members, err := portal.MainIntent().JoinedMembers(portal.MXID) + if err != nil { + portal.log.Warnln("Failed to get member list:", err) + } else { + for member := range members.Joined { + jid, ok := portal.bridge.ParsePuppetMXID(member) + if ok { + _, shouldBePresent := participantMap[jid] + if !shouldBePresent { + _, err := portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{ + UserID: member, + Reason: "User had left this WhatsApp chat", + }) + if err != nil { + portal.log.Warnfln("Failed to kick user %s who had left: %v", member, err) + } + } + } + } + } +} + +func (portal *Portal) SyncBroadcastRecipients(metadata *whatsapp.BroadcastListInfo) { + participantMap := make(map[whatsapp.JID]bool) + for _, recipient := range metadata.Recipients { + participantMap[recipient.JID] = true + + puppet := portal.bridge.GetPuppetByJID(recipient.JID) + err := puppet.DefaultIntent().EnsureJoined(portal.MXID) + if err != nil { + portal.log.Warnfln("Failed to make puppet of %s join %s: %v", recipient.JID, portal.MXID, err) + } + } + portal.kickExtraUsers(participantMap) +} + func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) { changed := false levels, err := portal.MainIntent().PowerLevels(portal.MXID) @@ -366,7 +403,7 @@ func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) { levels = portal.GetBasePowerLevels() changed = true } - participantMap := make(map[string]bool) + participantMap := make(map[whatsapp.JID]bool) for _, participant := range metadata.Participants { participantMap[participant.JID] = true user := portal.bridge.GetUserByJID(participant.JID) @@ -395,26 +432,7 @@ func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) { portal.log.Errorln("Failed to change power levels:", err) } } - members, err := portal.MainIntent().JoinedMembers(portal.MXID) - if err != nil { - portal.log.Warnln("Failed to get member list:", err) - } else { - for member := range members.Joined { - jid, ok := portal.bridge.ParsePuppetMXID(member) - if ok { - _, shouldBePresent := participantMap[jid] - if !shouldBePresent { - _, err := portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{ - UserID: member, - Reason: "User had left this WhatsApp chat", - }) - if err != nil { - portal.log.Warnfln("Failed to kick user %s who had left: %v", member, err) - } - } - } - } - } + portal.kickExtraUsers(participantMap) } func (portal *Portal) UpdateAvatar(user *User, avatar *whatsapp.ProfilePicInfo, updateInfo bool) bool { @@ -469,7 +487,7 @@ func (portal *Portal) UpdateAvatar(user *User, avatar *whatsapp.ProfilePicInfo, } func (portal *Portal) UpdateName(name string, setBy whatsapp.JID, intent *appservice.IntentAPI, updateInfo bool) bool { - if name == "" && portal.IsBroadcastRoom() { + if name == "" && portal.IsBroadcastList() { name = UnnamedBroadcastName } if portal.Name != name { @@ -522,12 +540,12 @@ func (portal *Portal) UpdateTopic(topic string, setBy whatsapp.JID, intent *apps func (portal *Portal) UpdateMetadata(user *User) bool { if portal.IsPrivateChat() { return false - } else if portal.IsStatusBroadcastRoom() { + } else if portal.IsStatusBroadcastList() { update := false update = portal.UpdateName(StatusBroadcastName, "", nil, false) || update update = portal.UpdateTopic(StatusBroadcastTopic, "", nil, false) || update return update - } else if portal.IsBroadcastRoom() { + } else if portal.IsBroadcastList() { update := false contact, _ := user.Conn.Store.Contacts[portal.Key.JID] update = portal.UpdateName(contact.Name, "", nil, false) || update @@ -612,7 +630,7 @@ func (portal *Portal) Sync(user *User, contact whatsapp.Contact) { update := false update = portal.UpdateMetadata(user) || update - if !portal.IsPrivateChat() && !portal.IsBroadcastRoom() && portal.Avatar == "" { + if !portal.IsPrivateChat() && !portal.IsBroadcastList() && portal.Avatar == "" { update = portal.UpdateAvatar(user, nil, false) || update } if update { @@ -988,6 +1006,7 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { portal.log.Infoln("Creating Matrix room. Info source:", user.MXID) var metadata *whatsapp.GroupInfo + var broadcastMetadata *whatsapp.BroadcastListInfo if portal.IsPrivateChat() { puppet := portal.bridge.GetPuppetByJID(portal.Key.JID) if portal.bridge.Config.Bridge.PrivateChatPortalMeta { @@ -998,14 +1017,19 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { portal.Name = "" } portal.Topic = "WhatsApp private chat" - } else if portal.IsStatusBroadcastRoom() { + } else if portal.IsStatusBroadcastList() { portal.Name = "WhatsApp Status Broadcast" portal.Topic = "WhatsApp status updates from your contacts" - } else if portal.IsBroadcastRoom() { - contact, ok := user.Conn.Store.Contacts[portal.Key.JID] - if ok { - portal.Name = contact.Name + } else if portal.IsBroadcastList() { + var err error + broadcastMetadata, err = user.Conn.GetBroadcastMetadata(portal.Key.JID) + if err == nil && broadcastMetadata.Status == 0 { + portal.Name = broadcastMetadata.Name } else { + contact, _ := user.Conn.Store.Contacts[portal.Key.JID] + portal.Name = contact.Name + } + if len(portal.Name) == 0 { portal.Name = UnnamedBroadcastName } portal.Topic = BroadcastTopic @@ -1097,6 +1121,9 @@ func (portal *Portal) CreateMatrixRoom(user *User) error { _ = customPuppet.CustomIntent().EnsureJoined(portal.MXID) } } + if broadcastMetadata != nil { + portal.SyncBroadcastRecipients(broadcastMetadata) + } user.addPortalToCommunity(portal) if portal.IsPrivateChat() { puppet := user.bridge.GetPuppetByJID(portal.Key.JID) @@ -1126,7 +1153,7 @@ func (portal *Portal) IsPrivateChat() bool { return *portal.isPrivate } -func (portal *Portal) IsBroadcastRoom() bool { +func (portal *Portal) IsBroadcastList() bool { if portal.isBroadcast == nil { val := strings.HasSuffix(portal.Key.JID, whatsapp.BroadcastSuffix) portal.isBroadcast = &val @@ -1134,7 +1161,7 @@ func (portal *Portal) IsBroadcastRoom() bool { return *portal.isBroadcast } -func (portal *Portal) IsStatusBroadcastRoom() bool { +func (portal *Portal) IsStatusBroadcastList() bool { return portal.Key.JID == "status@broadcast" } @@ -1310,8 +1337,9 @@ func (portal *Portal) HandleTextMessage(source *User, message whatsapp.TextMessa } func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessage, isBackfill bool) { - if portal.bridge.Config.Bridge.ChatMetaSync { + if portal.bridge.Config.Bridge.ChatMetaSync && (!portal.IsBroadcastList() || isBackfill) { // Chat meta sync is enabled, so we use chat update commands and full-syncs instead of message history + // However, broadcast lists don't have update commands, so we handle these if it's not a backfill return } intent := portal.startHandling(source, message.Info) @@ -1341,9 +1369,9 @@ func (portal *Portal) HandleStubMessage(source *User, message whatsapp.StubMessa eventID = portal.RestrictMessageSending(message.FirstParam == "on") case waProto.WebMessageInfo_GROUP_CHANGE_RESTRICT: eventID = portal.RestrictMetadataChanges(message.FirstParam == "on") - case waProto.WebMessageInfo_GROUP_PARTICIPANT_ADD, waProto.WebMessageInfo_GROUP_PARTICIPANT_INVITE: + case waProto.WebMessageInfo_GROUP_PARTICIPANT_ADD, waProto.WebMessageInfo_GROUP_PARTICIPANT_INVITE, waProto.WebMessageInfo_BROADCAST_ADD: portal.HandleWhatsAppInvite(senderJID, intent, message.Params) - case waProto.WebMessageInfo_GROUP_PARTICIPANT_REMOVE, waProto.WebMessageInfo_GROUP_PARTICIPANT_LEAVE: + case waProto.WebMessageInfo_GROUP_PARTICIPANT_REMOVE, waProto.WebMessageInfo_GROUP_PARTICIPANT_LEAVE, waProto.WebMessageInfo_BROADCAST_REMOVE: portal.HandleWhatsAppKick(source, senderJID, message.Params) case waProto.WebMessageInfo_GROUP_PARTICIPANT_PROMOTE: eventID = portal.ChangeAdminStatus(message.Params, true) @@ -1525,13 +1553,15 @@ func (portal *Portal) HandleWhatsAppKick(source *User, senderJID string, jids [] puppet := portal.bridge.GetPuppetByJID(jid) portal.removeUser(puppet.JID == sender.JID, senderIntent, puppet.MXID, puppet.DefaultIntent()) - user := portal.bridge.GetUserByJID(jid) - if user != nil { - var customIntent *appservice.IntentAPI - if puppet.CustomMXID == user.MXID { - customIntent = puppet.CustomIntent() + if !portal.IsBroadcastList() { + user := portal.bridge.GetUserByJID(jid) + if user != nil { + var customIntent *appservice.IntentAPI + if puppet.CustomMXID == user.MXID { + customIntent = puppet.CustomIntent() + } + portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent) } - portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent) } } }