Quellcode durchsuchen

Handle WhatsApp kicks and Matrix invites

Tulir Asokan vor 5 Jahren
Ursprung
Commit
326293303d
5 geänderte Dateien mit 91 neuen und 12 gelöschten Zeilen
  1. 2 2
      commands.go
  2. 5 1
      matrix.go
  3. 62 2
      portal.go
  4. 16 2
      user.go
  5. 6 5
      whatsapp-ext/chat.go

+ 2 - 2
commands.go

@@ -235,7 +235,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
 	portal := handler.bridge.GetPortalByJID(database.GroupPortalKey(jid))
 	if len(portal.MXID) > 0 {
 		portal.Sync(ce.User, whatsapp.Contact{Jid: portal.Key.JID})
-		ce.Reply("Successfully joined group \"%s\" and synced portal room", portal.Name)
+		ce.Reply("Successfully joined group \"%s\" and synced portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
 	} else {
 		err = portal.CreateMatrixRoom(ce.User)
 		if err != nil {
@@ -243,7 +243,7 @@ func (handler *CommandHandler) CommandJoin(ce *CommandEvent) {
 			return
 		}
 
-		ce.Reply("Successfully joined group \"%s\" and created portal room", portal.Name)
+		ce.Reply("Successfully joined group \"%s\" and created portal room: [%s](https://matrix.to/#/%s)", portal.Name, portal.Name, portal.MXID)
 	}
 }
 

+ 5 - 1
matrix.go

@@ -155,8 +155,10 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
 		return
 	}
 
+	isSelf := id.UserID(evt.GetStateKey()) == evt.Sender
+
 	if content.Membership == event.MembershipLeave {
-		if id.UserID(evt.GetStateKey()) == evt.Sender {
+		if isSelf {
 			if evt.Unsigned.PrevContent != nil {
 				_ = evt.Unsigned.PrevContent.ParseRaw(evt.Type)
 				prevContent, ok := evt.Unsigned.PrevContent.Parsed.(*event.MemberEventContent)
@@ -169,6 +171,8 @@ func (mx *MatrixHandler) HandleMembership(evt *event.Event) {
 		} else {
 			portal.HandleMatrixKick(user, evt)
 		}
+	} else if content.Membership == event.MembershipInvite && !isSelf {
+		portal.HandleMatrixInvite(user, evt)
 	}
 }
 

+ 62 - 2
portal.go

@@ -1281,6 +1281,53 @@ func (portal *Portal) encryptFile(data []byte, mimeType string) ([]byte, string,
 	return file.Encrypt(data), "application/octet-stream", file
 }
 
+func (portal *Portal) tryKickUser(userID id.UserID, intent *appservice.IntentAPI) error {
+	_, err := intent.KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: userID})
+	if err != nil {
+		httpErr, ok := err.(mautrix.HTTPError)
+		if ok && httpErr.RespError != nil && httpErr.RespError.ErrCode == "M_FORBIDDEN" {
+			_, err = portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: userID})
+		}
+	}
+	return err
+}
+
+func (portal *Portal) removeUser(isSameUser bool, kicker *appservice.IntentAPI, target id.UserID, targetIntent *appservice.IntentAPI) {
+	if !isSameUser || targetIntent == nil {
+		err := portal.tryKickUser(target, kicker)
+		if err != nil {
+			portal.log.Warnfln("Failed to kick %s from %s: %v", target, portal.MXID, err)
+			if targetIntent != nil {
+				_, _ = targetIntent.LeaveRoom(portal.MXID)
+			}
+		}
+	} else {
+		_, err := targetIntent.LeaveRoom(portal.MXID)
+		if err != nil {
+			portal.log.Warnfln("Failed to leave portal as %s: %v", target, err)
+			_, _ = portal.MainIntent().KickUser(portal.MXID, &mautrix.ReqKickUser{UserID: target})
+		}
+	}
+}
+
+func (portal *Portal) HandleWhatsAppKick(senderJID string, jids []string) {
+	sender := portal.bridge.GetPuppetByJID(senderJID)
+	senderIntent := sender.IntentFor(portal)
+	for _, jid := range 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()
+			}
+			portal.removeUser(puppet.JID == sender.JID, senderIntent, user.MXID, customIntent)
+		}
+	}
+}
+
 type base struct {
 	download func() ([]byte, error)
 	info     whatsapp.MessageInfo
@@ -2005,6 +2052,7 @@ func (portal *Portal) HandleMatrixLeave(sender *User) {
 		portal.Cleanup(false)
 		return
 	} else {
+		// TODO should we somehow deduplicate this call if this leave was sent by the bridge?
 		resp, err := sender.Conn.LeaveGroup(portal.Key.JID)
 		if err != nil {
 			portal.log.Errorfln("Failed to leave group as %s: %v", sender.MXID, err)
@@ -2015,8 +2063,8 @@ func (portal *Portal) HandleMatrixLeave(sender *User) {
 	}
 }
 
-func (portal *Portal) HandleMatrixKick(sender *User, event *event.Event) {
-	puppet := portal.bridge.GetPuppetByMXID(id.UserID(event.GetStateKey()))
+func (portal *Portal) HandleMatrixKick(sender *User, evt *event.Event) {
+	puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
 	if puppet != nil {
 		resp, err := sender.Conn.RemoveMember(portal.Key.JID, []string{puppet.JID})
 		if err != nil {
@@ -2026,3 +2074,15 @@ func (portal *Portal) HandleMatrixKick(sender *User, event *event.Event) {
 		portal.log.Infoln("Kick %s response: %s", puppet.JID, <-resp)
 	}
 }
+
+func (portal *Portal) HandleMatrixInvite(sender *User, evt *event.Event) {
+	puppet := portal.bridge.GetPuppetByMXID(id.UserID(evt.GetStateKey()))
+	if puppet != nil {
+		resp, err := sender.Conn.AddMember(portal.Key.JID, []string{puppet.JID})
+		if err != nil {
+			portal.log.Errorfln("Failed to add %s to group as %s: %v", puppet.JID, sender.MXID, err)
+			return
+		}
+		portal.log.Infoln("Add %s response: %s", puppet.JID, <-resp)
+	}
+}

+ 16 - 2
user.go

@@ -861,6 +861,14 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
 
 	portal := user.GetPortalByJID(cmd.JID)
 	if len(portal.MXID) == 0 {
+		if cmd.Data.Action == whatsappExt.ChatActionIntroduce && cmd.Data.SenderJID != "unknown" {
+			go func() {
+				err := portal.CreateMatrixRoom(user)
+				if err != nil {
+					user.log.Errorln("Failed to create portal room after receiving join event:", err)
+				}
+			}()
+		}
 		return
 	}
 
@@ -872,13 +880,19 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
 	case whatsappExt.ChatActionRemoveTopic:
 		go portal.UpdateTopic("", cmd.Data.SenderJID, true)
 	case whatsappExt.ChatActionPromote:
-		go portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, true)
+		go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, true)
 	case whatsappExt.ChatActionDemote:
-		go portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, false)
+		go portal.ChangeAdminStatus(cmd.Data.UserChange.JIDs, false)
 	case whatsappExt.ChatActionAnnounce:
 		go portal.RestrictMessageSending(cmd.Data.Announce)
 	case whatsappExt.ChatActionRestrict:
 		go portal.RestrictMetadataChanges(cmd.Data.Restrict)
+	case whatsappExt.ChatActionRemove:
+		go portal.HandleWhatsAppKick(cmd.Data.SenderJID, cmd.Data.UserChange.JIDs)
+	case whatsappExt.ChatActionIntroduce:
+		if cmd.Data.SenderJID != "unknown" {
+			go portal.Sync(user, whatsapp.Contact{Jid: portal.Key.JID})
+		}
 	}
 }
 

+ 6 - 5
whatsapp-ext/chat.go

@@ -46,6 +46,7 @@ const (
 	ChatActionPromote     ChatActionType = "promote"
 	ChatActionDemote      ChatActionType = "demote"
 	ChatActionIntroduce   ChatActionType = "introduce"
+	ChatActionRemove      ChatActionType = "remove"
 )
 
 type ChatUpdateData struct {
@@ -80,7 +81,7 @@ type ChatUpdateData struct {
 
 	Announce bool
 
-	PermissionChange struct {
+	UserChange struct {
 		JIDs []string `json:"participants"`
 	}
 }
@@ -127,8 +128,8 @@ func (cud *ChatUpdateData) UnmarshalJSON(data []byte) error {
 		unmarshalTo = &cud.Restrict
 	case ChatActionAnnounce:
 		unmarshalTo = &cud.Announce
-	case ChatActionPromote, ChatActionDemote:
-		unmarshalTo = &cud.PermissionChange
+	case ChatActionPromote, ChatActionDemote, ChatActionRemove:
+		unmarshalTo = &cud.UserChange
 	default:
 		return nil
 	}
@@ -137,8 +138,8 @@ func (cud *ChatUpdateData) UnmarshalJSON(data []byte) error {
 		return err
 	}
 	cud.NameChange.SetBy = strings.Replace(cud.NameChange.SetBy, OldUserSuffix, NewUserSuffix, 1)
-	for index, jid := range cud.PermissionChange.JIDs {
-		cud.PermissionChange.JIDs[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
+	for index, jid := range cud.UserChange.JIDs {
+		cud.UserChange.JIDs[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)
 	}
 	for index, jid := range cud.Introduce.SuperAdmins {
 		cud.Introduce.SuperAdmins[index] = strings.Replace(jid, OldUserSuffix, NewUserSuffix, 1)