Sync broadcast list recipients too

This commit is contained in:
Tulir Asokan 2021-02-21 14:45:33 +02:00
parent a911a0c1a9
commit ac2ca08007
4 changed files with 75 additions and 43 deletions

View file

@ -164,7 +164,7 @@ func (puppet *Puppet) ProcessResponse(resp *mautrix.RespSync, _ string) error {
} }
for roomID, events := range resp.Rooms.Join { for roomID, events := range resp.Rooms.Join {
portal := puppet.bridge.GetPortalByMXID(roomID) portal := puppet.bridge.GetPortalByMXID(roomID)
if portal == nil { if portal == nil || portal.IsBroadcastList() {
continue continue
} }
for _, evt := range events.Ephemeral.Events { for _, evt := range events.Ephemeral.Events {

2
go.mod
View file

@ -16,4 +16,4 @@ require (
maunium.net/go/mautrix v0.8.3 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

2
go.sum
View file

@ -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.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 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.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.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 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= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=

100
portal.go
View file

@ -359,6 +359,43 @@ func (portal *Portal) finishHandling(source *User, message *waProto.WebMessageIn
portal.log.Debugln("Handled message", message.GetKey().GetId(), "->", mxid) 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) { func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) {
changed := false changed := false
levels, err := portal.MainIntent().PowerLevels(portal.MXID) levels, err := portal.MainIntent().PowerLevels(portal.MXID)
@ -366,7 +403,7 @@ func (portal *Portal) SyncParticipants(metadata *whatsapp.GroupInfo) {
levels = portal.GetBasePowerLevels() levels = portal.GetBasePowerLevels()
changed = true changed = true
} }
participantMap := make(map[string]bool) participantMap := make(map[whatsapp.JID]bool)
for _, participant := range metadata.Participants { for _, participant := range metadata.Participants {
participantMap[participant.JID] = true participantMap[participant.JID] = true
user := portal.bridge.GetUserByJID(participant.JID) 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) portal.log.Errorln("Failed to change power levels:", err)
} }
} }
members, err := portal.MainIntent().JoinedMembers(portal.MXID) portal.kickExtraUsers(participantMap)
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) UpdateAvatar(user *User, avatar *whatsapp.ProfilePicInfo, updateInfo bool) bool { 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 { 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 name = UnnamedBroadcastName
} }
if portal.Name != name { 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 { func (portal *Portal) UpdateMetadata(user *User) bool {
if portal.IsPrivateChat() { if portal.IsPrivateChat() {
return false return false
} else if portal.IsStatusBroadcastRoom() { } else if portal.IsStatusBroadcastList() {
update := false update := false
update = portal.UpdateName(StatusBroadcastName, "", nil, false) || update update = portal.UpdateName(StatusBroadcastName, "", nil, false) || update
update = portal.UpdateTopic(StatusBroadcastTopic, "", nil, false) || update update = portal.UpdateTopic(StatusBroadcastTopic, "", nil, false) || update
return update return update
} else if portal.IsBroadcastRoom() { } else if portal.IsBroadcastList() {
update := false update := false
contact, _ := user.Conn.Store.Contacts[portal.Key.JID] contact, _ := user.Conn.Store.Contacts[portal.Key.JID]
update = portal.UpdateName(contact.Name, "", nil, false) || update update = portal.UpdateName(contact.Name, "", nil, false) || update
@ -612,7 +630,7 @@ func (portal *Portal) Sync(user *User, contact whatsapp.Contact) {
update := false update := false
update = portal.UpdateMetadata(user) || update 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 update = portal.UpdateAvatar(user, nil, false) || update
} }
if update { if update {
@ -988,6 +1006,7 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
portal.log.Infoln("Creating Matrix room. Info source:", user.MXID) portal.log.Infoln("Creating Matrix room. Info source:", user.MXID)
var metadata *whatsapp.GroupInfo var metadata *whatsapp.GroupInfo
var broadcastMetadata *whatsapp.BroadcastListInfo
if portal.IsPrivateChat() { if portal.IsPrivateChat() {
puppet := portal.bridge.GetPuppetByJID(portal.Key.JID) puppet := portal.bridge.GetPuppetByJID(portal.Key.JID)
if portal.bridge.Config.Bridge.PrivateChatPortalMeta { if portal.bridge.Config.Bridge.PrivateChatPortalMeta {
@ -998,14 +1017,19 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
portal.Name = "" portal.Name = ""
} }
portal.Topic = "WhatsApp private chat" portal.Topic = "WhatsApp private chat"
} else if portal.IsStatusBroadcastRoom() { } else if portal.IsStatusBroadcastList() {
portal.Name = "WhatsApp Status Broadcast" portal.Name = "WhatsApp Status Broadcast"
portal.Topic = "WhatsApp status updates from your contacts" portal.Topic = "WhatsApp status updates from your contacts"
} else if portal.IsBroadcastRoom() { } else if portal.IsBroadcastList() {
contact, ok := user.Conn.Store.Contacts[portal.Key.JID] var err error
if ok { broadcastMetadata, err = user.Conn.GetBroadcastMetadata(portal.Key.JID)
portal.Name = contact.Name if err == nil && broadcastMetadata.Status == 0 {
portal.Name = broadcastMetadata.Name
} else { } else {
contact, _ := user.Conn.Store.Contacts[portal.Key.JID]
portal.Name = contact.Name
}
if len(portal.Name) == 0 {
portal.Name = UnnamedBroadcastName portal.Name = UnnamedBroadcastName
} }
portal.Topic = BroadcastTopic portal.Topic = BroadcastTopic
@ -1097,6 +1121,9 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
_ = customPuppet.CustomIntent().EnsureJoined(portal.MXID) _ = customPuppet.CustomIntent().EnsureJoined(portal.MXID)
} }
} }
if broadcastMetadata != nil {
portal.SyncBroadcastRecipients(broadcastMetadata)
}
user.addPortalToCommunity(portal) user.addPortalToCommunity(portal)
if portal.IsPrivateChat() { if portal.IsPrivateChat() {
puppet := user.bridge.GetPuppetByJID(portal.Key.JID) puppet := user.bridge.GetPuppetByJID(portal.Key.JID)
@ -1126,7 +1153,7 @@ func (portal *Portal) IsPrivateChat() bool {
return *portal.isPrivate return *portal.isPrivate
} }
func (portal *Portal) IsBroadcastRoom() bool { func (portal *Portal) IsBroadcastList() bool {
if portal.isBroadcast == nil { if portal.isBroadcast == nil {
val := strings.HasSuffix(portal.Key.JID, whatsapp.BroadcastSuffix) val := strings.HasSuffix(portal.Key.JID, whatsapp.BroadcastSuffix)
portal.isBroadcast = &val portal.isBroadcast = &val
@ -1134,7 +1161,7 @@ func (portal *Portal) IsBroadcastRoom() bool {
return *portal.isBroadcast return *portal.isBroadcast
} }
func (portal *Portal) IsStatusBroadcastRoom() bool { func (portal *Portal) IsStatusBroadcastList() bool {
return portal.Key.JID == "status@broadcast" 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) { 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 // 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 return
} }
intent := portal.startHandling(source, message.Info) 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") eventID = portal.RestrictMessageSending(message.FirstParam == "on")
case waProto.WebMessageInfo_GROUP_CHANGE_RESTRICT: case waProto.WebMessageInfo_GROUP_CHANGE_RESTRICT:
eventID = portal.RestrictMetadataChanges(message.FirstParam == "on") 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) 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) portal.HandleWhatsAppKick(source, senderJID, message.Params)
case waProto.WebMessageInfo_GROUP_PARTICIPANT_PROMOTE: case waProto.WebMessageInfo_GROUP_PARTICIPANT_PROMOTE:
eventID = portal.ChangeAdminStatus(message.Params, true) eventID = portal.ChangeAdminStatus(message.Params, true)
@ -1525,6 +1553,7 @@ func (portal *Portal) HandleWhatsAppKick(source *User, senderJID string, jids []
puppet := portal.bridge.GetPuppetByJID(jid) puppet := portal.bridge.GetPuppetByJID(jid)
portal.removeUser(puppet.JID == sender.JID, senderIntent, puppet.MXID, puppet.DefaultIntent()) portal.removeUser(puppet.JID == sender.JID, senderIntent, puppet.MXID, puppet.DefaultIntent())
if !portal.IsBroadcastList() {
user := portal.bridge.GetUserByJID(jid) user := portal.bridge.GetUserByJID(jid)
if user != nil { if user != nil {
var customIntent *appservice.IntentAPI var customIntent *appservice.IntentAPI
@ -1535,6 +1564,7 @@ func (portal *Portal) HandleWhatsAppKick(source *User, senderJID string, jids []
} }
} }
} }
}
func (portal *Portal) HandleWhatsAppInvite(senderJID string, intent *appservice.IntentAPI, jids []string) { func (portal *Portal) HandleWhatsAppInvite(senderJID string, intent *appservice.IntentAPI, jids []string) {
if intent == nil { if intent == nil {