Эх сурвалжийг харах

Add option to update m.direct with double puppeting

Tulir Asokan 4 жил өмнө
parent
commit
2638204eaa
11 өөрчлөгдсөн 87 нэмэгдсэн , 12 устгасан
  1. 1 0
      config/bridge.go
  2. 1 0
      config/config.go
  3. 2 2
      crypto.go
  4. 2 2
      custompuppet.go
  5. 4 0
      database/portal.go
  6. 4 0
      example-config.yaml
  7. 1 1
      go.mod
  8. 2 0
      go.sum
  9. 2 0
      portal.go
  10. 10 6
      puppet.go
  11. 58 1
      user.go

+ 1 - 0
config/bridge.go

@@ -60,6 +60,7 @@ type BridgeConfig struct {
 	SyncChatMaxAge       uint64 `yaml:"sync_max_chat_age"`
 
 	SyncWithCustomPuppets bool   `yaml:"sync_with_custom_puppets"`
+	SyncDirectChatList    bool   `yaml:"sync_direct_chat_list"`
 	DefaultBridgeReceipts bool   `yaml:"default_bridge_receipts"`
 	DefaultBridgePresence bool   `yaml:"default_bridge_presence"`
 	LoginSharedSecret     string `yaml:"login_shared_secret"`

+ 1 - 0
config/config.go

@@ -28,6 +28,7 @@ type Config struct {
 	Homeserver struct {
 		Address string `yaml:"address"`
 		Domain  string `yaml:"domain"`
+		Asmux   bool   `yaml:"asmux"`
 	} `yaml:"homeserver"`
 
 	AppService struct {

+ 2 - 2
crypto.go

@@ -128,8 +128,8 @@ func (helper *CryptoHelper) loginBot() (*mautrix.Client, error) {
 		return nil, err
 	}
 	resp, err := client.Login(&mautrix.ReqLogin{
-		Type:                     "m.login.password",
-		Identifier:               mautrix.UserIdentifier{Type: "m.id.user", User: string(helper.bridge.AS.BotMXID())},
+		Type:                     mautrix.AuthTypePassword,
+		Identifier:               mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: string(helper.bridge.AS.BotMXID())},
 		Password:                 hex.EncodeToString(mac.Sum(nil)),
 		DeviceID:                 deviceID,
 		InitialDeviceDisplayName: "WhatsApp Bridge",

+ 2 - 2
custompuppet.go

@@ -68,8 +68,8 @@ func (puppet *Puppet) loginWithSharedSecret(mxid id.UserID) (string, error) {
 	mac := hmac.New(sha512.New, []byte(puppet.bridge.Config.Bridge.LoginSharedSecret))
 	mac.Write([]byte(mxid))
 	resp, err := puppet.bridge.AS.BotClient().Login(&mautrix.ReqLogin{
-		Type:                     "m.login.password",
-		Identifier:               mautrix.UserIdentifier{Type: "m.id.user", User: string(mxid)},
+		Type:                     mautrix.AuthTypePassword,
+		Identifier:               mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: string(mxid)},
 		Password:                 hex.EncodeToString(mac.Sum(nil)),
 		DeviceID:                 "WhatsApp Bridge",
 		InitialDeviceDisplayName: "WhatsApp Bridge",

+ 4 - 0
database/portal.go

@@ -84,6 +84,10 @@ func (pq *PortalQuery) GetAllByJID(jid types.WhatsAppID) []*Portal {
 	return pq.getAll("SELECT * FROM portal WHERE jid=$1", jid)
 }
 
+func (pq *PortalQuery) FindPrivateChats(receiver types.WhatsAppID) []*Portal {
+	return pq.getAll("SELECT * FROM portal WHERE receiver=$1 AND jid LIKE '%@s.whatsapp.net'", receiver)
+}
+
 func (pq *PortalQuery) getAll(query string, args ...interface{}) (portals []*Portal) {
 	rows, err := pq.db.Query(query, args...)
 	if err != nil || rows == nil {

+ 4 - 0
example-config.yaml

@@ -138,6 +138,10 @@ bridge:
     # Whether or not to sync with custom puppets to receive EDUs that
     # are not normally sent to appservices.
     sync_with_custom_puppets: true
+    # Whether or not to update the m.direct account data event when double puppeting is enabled.
+    # Note that updating the m.direct event is not atomic (except with mautrix-asmux)
+    # and is therefore prone to race conditions.
+    sync_direct_chat_list: false
     # When double puppeting is enabled, users can use `!wa toggle` to change whether or not
     # presence and read receipts are bridged. These settings set the default values.
     # Existing users won't be affected when these are changed.

+ 1 - 1
go.mod

@@ -16,7 +16,7 @@ require (
 	gopkg.in/yaml.v2 v2.3.0
 	maunium.net/go/mauflag v1.0.0
 	maunium.net/go/maulogger/v2 v2.1.1
-	maunium.net/go/mautrix v0.7.0
+	maunium.net/go/mautrix v0.7.2
 )
 
 replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.7

+ 2 - 0
go.sum

@@ -221,3 +221,5 @@ maunium.net/go/mautrix v0.7.0-rc.3 h1:GVmrVvY5vDASMyZ2xJ9kNynWsgqKl1yerKP7c6RsM7
 maunium.net/go/mautrix v0.7.0-rc.3/go.mod h1:Va/74MijqaS0DQ3aUqxmFO54/PMfr1LVsCOcGRHbYmo=
 maunium.net/go/mautrix v0.7.0 h1:9Wxs5S4Wl4S99dbBwfLZYAe/sP7VKaFikw9Ocf88kfk=
 maunium.net/go/mautrix v0.7.0/go.mod h1:Va/74MijqaS0DQ3aUqxmFO54/PMfr1LVsCOcGRHbYmo=
+maunium.net/go/mautrix v0.7.2 h1:ru//jj7Y5Xj9CXBpeNyWCoxjq8iT0d+a2lNeSiN9P/o=
+maunium.net/go/mautrix v0.7.2/go.mod h1:Va/74MijqaS0DQ3aUqxmFO54/PMfr1LVsCOcGRHbYmo=

+ 2 - 0
portal.go

@@ -1038,6 +1038,8 @@ func (portal *Portal) CreateMatrixRoom(user *User) error {
 				portal.log.Errorln("Failed to join created portal with bridge bot for e2be:", err)
 			}
 		}
+
+		user.UpdateDirectChats(map[id.UserID][]id.RoomID{puppet.MXID: {portal.MXID}})
 	}
 	err = portal.FillInitialHistory(user)
 	if err != nil {

+ 10 - 6
puppet.go

@@ -124,18 +124,22 @@ func (bridge *Bridge) dbPuppetsToPuppets(dbPuppets []*database.Puppet) []*Puppet
 	return output
 }
 
+func (bridge *Bridge) FormatPuppetMXID(jid types.WhatsAppID) id.UserID {
+	return id.NewUserID(
+		bridge.Config.Bridge.FormatUsername(
+			strings.Replace(
+				jid,
+				whatsappExt.NewUserSuffix, "", 1)),
+		bridge.Config.Homeserver.Domain)
+}
+
 func (bridge *Bridge) NewPuppet(dbPuppet *database.Puppet) *Puppet {
 	return &Puppet{
 		Puppet: dbPuppet,
 		bridge: bridge,
 		log:    bridge.Log.Sub(fmt.Sprintf("Puppet/%s", dbPuppet.JID)),
 
-		MXID: id.NewUserID(
-			bridge.Config.Bridge.FormatUsername(
-				strings.Replace(
-					dbPuppet.JID,
-					whatsappExt.NewUserSuffix, "", 1)),
-			bridge.Config.Homeserver.Domain),
+		MXID: bridge.FormatPuppetMXID(dbPuppet.JID),
 	}
 }
 

+ 58 - 1
user.go

@@ -19,6 +19,7 @@ package main
 import (
 	"encoding/json"
 	"fmt"
+	"net/http"
 	"sort"
 	"strconv"
 	"strings"
@@ -28,12 +29,12 @@ import (
 	"github.com/pkg/errors"
 	"github.com/skip2/go-qrcode"
 	log "maunium.net/go/maulogger/v2"
-	"maunium.net/go/mautrix"
 
 	"github.com/Rhymen/go-whatsapp"
 	waBinary "github.com/Rhymen/go-whatsapp/binary"
 	waProto "github.com/Rhymen/go-whatsapp/binary/proto"
 
+	"maunium.net/go/mautrix"
 	"maunium.net/go/mautrix/event"
 	"maunium.net/go/mautrix/format"
 	"maunium.net/go/mautrix/id"
@@ -573,6 +574,7 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
 			}
 		}
 	}
+	user.UpdateDirectChats(nil)
 	user.log.Infoln("Finished syncing portals")
 	select {
 	case user.syncPortalsDone <- struct{}{}:
@@ -580,6 +582,61 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
 	}
 }
 
+func (user *User) getDirectChats() map[id.UserID][]id.RoomID {
+	res := make(map[id.UserID][]id.RoomID)
+	privateChats := user.bridge.DB.Portal.FindPrivateChats(user.JID)
+	for _, portal := range privateChats {
+		if len(portal.MXID) > 0 {
+			res[user.bridge.FormatPuppetMXID(portal.Key.JID)] = []id.RoomID{portal.MXID}
+		}
+	}
+	return res
+}
+
+func (user *User) UpdateDirectChats(chats map[id.UserID][]id.RoomID) {
+	if !user.bridge.Config.Bridge.SyncDirectChatList {
+		return
+	}
+	puppet := user.bridge.GetPuppetByCustomMXID(user.MXID)
+	if puppet == nil || puppet.CustomIntent() == nil {
+		return
+	}
+	intent := puppet.CustomIntent()
+	method := http.MethodPatch
+	if chats == nil {
+		chats = user.getDirectChats()
+		method = http.MethodPut
+	}
+	user.log.Debugln("Updating m.direct list on homeserver")
+	var err error
+	if user.bridge.Config.Homeserver.Asmux {
+		urlPath := intent.BuildBaseURL("_matrix", "client", "unstable", "net.maunium.asmux", "dms")
+		_, err = intent.MakeFullRequest(method, urlPath, http.Header{
+			"X-Asmux-Auth": {user.bridge.AS.Registration.AppToken},
+		}, chats, nil)
+	} else {
+		existingChats := make(map[id.UserID][]id.RoomID)
+		err = intent.GetAccountData(event.AccountDataDirectChats.Type, &existingChats)
+		if err != nil {
+			user.log.Warnln("Failed to get m.direct list to update it:", err)
+			return
+		}
+		for userID, rooms := range existingChats {
+			if _, ok := user.bridge.ParsePuppetMXID(userID); !ok {
+				// This is not a ghost user, include it in the new list
+				chats[userID] = rooms
+			} else if _, ok := chats[userID]; !ok && method == http.MethodPatch {
+				// This is a ghost user, but we're not replacing the whole list, so include it too
+				chats[userID] = rooms
+			}
+		}
+		err = intent.SetAccountData(event.AccountDataDirectChats.Type, &chats)
+	}
+	if err != nil {
+		user.log.Warnln("Failed to update m.direct list:", err)
+	}
+}
+
 func (user *User) HandleContactList(contacts []whatsapp.Contact) {
 	contactMap := make(map[string]whatsapp.Contact)
 	for _, contact := range contacts {