Browse Source

Implement WhatsApp->Matrix power level bridging

Tulir Asokan 6 years ago
parent
commit
f78fb72622
5 changed files with 115 additions and 10 deletions
  1. 1 1
      ROADMAP.md
  2. 100 3
      portal.go
  3. 1 1
      statestore.go
  4. 8 1
      user.go
  5. 5 4
      whatsapp-ext/whatsapp.go

+ 1 - 1
ROADMAP.md

@@ -31,7 +31,7 @@
   * [x] Presence
   * [x] Typing notifications
   * [x] Read receipts
-  * [ ] Admin/superadmin status
+  * [x] Admin/superadmin status
   * [ ] Membership actions
     * [ ] Invite
     * [ ] Join

+ 100 - 3
portal.go

@@ -119,10 +119,29 @@ type Portal struct {
 }
 
 func (portal *Portal) SyncParticipants(metadata *whatsappExt.GroupInfo) {
+	changed := false
+	levels, err := portal.MainIntent().PowerLevels(portal.MXID)
+	if err != nil {
+		levels = portal.GetBasePowerLevels()
+		changed = true
+	}
 	for _, participant := range metadata.Participants {
-		intent := portal.user.GetPuppetByJID(participant.JID).Intent()
-		intent.EnsureJoined(portal.MXID)
-		// TODO set power levels
+		puppet := portal.user.GetPuppetByJID(participant.JID)
+		puppet.Intent().EnsureJoined(portal.MXID)
+		level := levels.GetUserLevel(puppet.MXID)
+		expectedLevel := 0
+		if participant.IsSuperAdmin {
+			expectedLevel = 95
+		} else if participant.IsAdmin {
+			expectedLevel = 50
+		}
+		if level != expectedLevel {
+			levels.SetUserLevel(puppet.MXID, expectedLevel)
+			changed = true
+		}
+	}
+	if changed {
+		portal.MainIntent().SetPowerLevels(portal.MXID, levels)
 	}
 }
 
@@ -225,6 +244,77 @@ func (portal *Portal) Sync(contact whatsapp.Contact) {
 	}
 }
 
+func (portal *Portal) GetBasePowerLevels() *gomatrix.PowerLevels {
+	anyone := 0
+	nope := 99
+	return &gomatrix.PowerLevels{
+		UsersDefault:    anyone,
+		EventsDefault:   anyone,
+		RedactPtr:       &anyone,
+		StateDefaultPtr: &nope,
+		BanPtr:          &nope,
+		InvitePtr:       &nope,
+		Users: map[string]int{
+			portal.MainIntent().UserID: 100,
+		},
+		Events: map[gomatrix.EventType]int{
+			gomatrix.StateRoomName:   anyone,
+			gomatrix.StateRoomAvatar: anyone,
+			gomatrix.StateTopic:      anyone,
+		},
+	}
+}
+
+func (portal *Portal) ChangeAdminStatus(jids []string, setAdmin bool) {
+	levels, err := portal.MainIntent().PowerLevels(portal.MXID)
+	if err != nil {
+		levels = portal.GetBasePowerLevels()
+	}
+	newLevel := 0
+	if setAdmin {
+		newLevel = 50
+	}
+	changed := false
+	for _, jid := range jids {
+		puppet := portal.user.GetPuppetByJID(jid)
+		changed = levels.EnsureUserLevel(puppet.MXID, newLevel) || changed
+	}
+	if changed {
+		portal.MainIntent().SetPowerLevels(portal.MXID, levels)
+	}
+}
+
+func (portal *Portal) RestrictMessageSending(restrict bool) {
+	levels, err := portal.MainIntent().PowerLevels(portal.MXID)
+	if err != nil {
+		levels = portal.GetBasePowerLevels()
+	}
+	if restrict {
+		levels.EventsDefault = 50
+	} else {
+		levels.EventsDefault = 0
+	}
+	portal.MainIntent().SetPowerLevels(portal.MXID, levels)
+}
+
+func (portal *Portal) RestrictMetadataChanges(restrict bool) {
+	levels, err := portal.MainIntent().PowerLevels(portal.MXID)
+	if err != nil {
+		levels = portal.GetBasePowerLevels()
+	}
+	newLevel := 0
+	if restrict {
+		newLevel = 50
+	}
+	changed := false
+	changed = levels.EnsureEventLevel(gomatrix.StateRoomName, true, newLevel) || changed
+	changed = levels.EnsureEventLevel(gomatrix.StateRoomAvatar, true, newLevel) || changed
+	changed = levels.EnsureEventLevel(gomatrix.StateTopic, true, newLevel) || changed
+	if changed {
+		portal.MainIntent().SetPowerLevels(portal.MXID, levels)
+	}
+}
+
 func (portal *Portal) CreateMatrixRoom() error {
 	portal.roomCreateLock.Lock()
 	defer portal.roomCreateLock.Unlock()
@@ -241,6 +331,7 @@ func (portal *Portal) CreateMatrixRoom() error {
 		topic = "WhatsApp private chat"
 		isPrivateChat = true
 	}
+
 	resp, err := portal.MainIntent().CreateRoom(&gomatrix.ReqCreateRoom{
 		Visibility: "private",
 		Name:       name,
@@ -248,6 +339,12 @@ func (portal *Portal) CreateMatrixRoom() error {
 		Invite:     invite,
 		Preset:     "private_chat",
 		IsDirect:   isPrivateChat,
+		InitialState: []*gomatrix.Event{{
+			Type: gomatrix.StatePowerLevels,
+			Content: gomatrix.Content{
+				PowerLevels: portal.GetBasePowerLevels(),
+			},
+		}},
 	})
 	if err != nil {
 		return err

+ 1 - 1
statestore.go

@@ -67,7 +67,7 @@ func (store *AutosavingStateStore) SetMembership(roomID, userID, membership stri
 	store.Save()
 }
 
-func (store *AutosavingStateStore) SetPowerLevels(roomID string, levels gomatrix.PowerLevels) {
+func (store *AutosavingStateStore) SetPowerLevels(roomID string, levels *gomatrix.PowerLevels) {
 	store.BasicStateStore.SetPowerLevels(roomID, levels)
 	store.Save()
 }

+ 8 - 1
user.go

@@ -321,7 +321,14 @@ func (user *User) HandleChatUpdate(cmd whatsappExt.ChatUpdate) {
 		portal.UpdateTopic(cmd.Data.AddTopic.Topic, cmd.Data.SenderJID)
 	case whatsappExt.ChatActionRemoveTopic:
 		portal.UpdateTopic("", cmd.Data.SenderJID)
-	// TODO power level updates
+	case whatsappExt.ChatActionPromote:
+		portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, true)
+	case whatsappExt.ChatActionDemote:
+		portal.ChangeAdminStatus(cmd.Data.PermissionChange.JIDs, false)
+	case whatsappExt.ChatActionAnnounce:
+		portal.RestrictMessageSending(cmd.Data.Announce)
+	case whatsappExt.ChatActionRestrict:
+		portal.RestrictMetadataChanges(cmd.Data.Restrict)
 	}
 }
 

+ 5 - 4
whatsapp-ext/whatsapp.go

@@ -17,13 +17,14 @@
 package whatsappExt
 
 import (
-	"fmt"
 	"encoding/json"
-	"github.com/Rhymen/go-whatsapp"
-	"net/http"
-	"io/ioutil"
+	"fmt"
 	"io"
+	"io/ioutil"
+	"net/http"
 	"strings"
+
+	"github.com/Rhymen/go-whatsapp"
 )
 
 const (