Add option to update m.direct with double puppeting
This commit is contained in:
parent
b1c947da99
commit
2638204eaa
11 changed files with 87 additions and 12 deletions
|
@ -60,6 +60,7 @@ type BridgeConfig struct {
|
||||||
SyncChatMaxAge uint64 `yaml:"sync_max_chat_age"`
|
SyncChatMaxAge uint64 `yaml:"sync_max_chat_age"`
|
||||||
|
|
||||||
SyncWithCustomPuppets bool `yaml:"sync_with_custom_puppets"`
|
SyncWithCustomPuppets bool `yaml:"sync_with_custom_puppets"`
|
||||||
|
SyncDirectChatList bool `yaml:"sync_direct_chat_list"`
|
||||||
DefaultBridgeReceipts bool `yaml:"default_bridge_receipts"`
|
DefaultBridgeReceipts bool `yaml:"default_bridge_receipts"`
|
||||||
DefaultBridgePresence bool `yaml:"default_bridge_presence"`
|
DefaultBridgePresence bool `yaml:"default_bridge_presence"`
|
||||||
LoginSharedSecret string `yaml:"login_shared_secret"`
|
LoginSharedSecret string `yaml:"login_shared_secret"`
|
||||||
|
|
|
@ -28,6 +28,7 @@ type Config struct {
|
||||||
Homeserver struct {
|
Homeserver struct {
|
||||||
Address string `yaml:"address"`
|
Address string `yaml:"address"`
|
||||||
Domain string `yaml:"domain"`
|
Domain string `yaml:"domain"`
|
||||||
|
Asmux bool `yaml:"asmux"`
|
||||||
} `yaml:"homeserver"`
|
} `yaml:"homeserver"`
|
||||||
|
|
||||||
AppService struct {
|
AppService struct {
|
||||||
|
|
|
@ -128,8 +128,8 @@ func (helper *CryptoHelper) loginBot() (*mautrix.Client, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
resp, err := client.Login(&mautrix.ReqLogin{
|
resp, err := client.Login(&mautrix.ReqLogin{
|
||||||
Type: "m.login.password",
|
Type: mautrix.AuthTypePassword,
|
||||||
Identifier: mautrix.UserIdentifier{Type: "m.id.user", User: string(helper.bridge.AS.BotMXID())},
|
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: string(helper.bridge.AS.BotMXID())},
|
||||||
Password: hex.EncodeToString(mac.Sum(nil)),
|
Password: hex.EncodeToString(mac.Sum(nil)),
|
||||||
DeviceID: deviceID,
|
DeviceID: deviceID,
|
||||||
InitialDeviceDisplayName: "WhatsApp Bridge",
|
InitialDeviceDisplayName: "WhatsApp Bridge",
|
||||||
|
|
|
@ -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 := hmac.New(sha512.New, []byte(puppet.bridge.Config.Bridge.LoginSharedSecret))
|
||||||
mac.Write([]byte(mxid))
|
mac.Write([]byte(mxid))
|
||||||
resp, err := puppet.bridge.AS.BotClient().Login(&mautrix.ReqLogin{
|
resp, err := puppet.bridge.AS.BotClient().Login(&mautrix.ReqLogin{
|
||||||
Type: "m.login.password",
|
Type: mautrix.AuthTypePassword,
|
||||||
Identifier: mautrix.UserIdentifier{Type: "m.id.user", User: string(mxid)},
|
Identifier: mautrix.UserIdentifier{Type: mautrix.IdentifierTypeUser, User: string(mxid)},
|
||||||
Password: hex.EncodeToString(mac.Sum(nil)),
|
Password: hex.EncodeToString(mac.Sum(nil)),
|
||||||
DeviceID: "WhatsApp Bridge",
|
DeviceID: "WhatsApp Bridge",
|
||||||
InitialDeviceDisplayName: "WhatsApp Bridge",
|
InitialDeviceDisplayName: "WhatsApp Bridge",
|
||||||
|
|
|
@ -84,6 +84,10 @@ func (pq *PortalQuery) GetAllByJID(jid types.WhatsAppID) []*Portal {
|
||||||
return pq.getAll("SELECT * FROM portal WHERE jid=$1", jid)
|
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) {
|
func (pq *PortalQuery) getAll(query string, args ...interface{}) (portals []*Portal) {
|
||||||
rows, err := pq.db.Query(query, args...)
|
rows, err := pq.db.Query(query, args...)
|
||||||
if err != nil || rows == nil {
|
if err != nil || rows == nil {
|
||||||
|
|
|
@ -138,6 +138,10 @@ bridge:
|
||||||
# Whether or not to sync with custom puppets to receive EDUs that
|
# Whether or not to sync with custom puppets to receive EDUs that
|
||||||
# are not normally sent to appservices.
|
# are not normally sent to appservices.
|
||||||
sync_with_custom_puppets: true
|
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
|
# 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.
|
# presence and read receipts are bridged. These settings set the default values.
|
||||||
# Existing users won't be affected when these are changed.
|
# Existing users won't be affected when these are changed.
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -16,7 +16,7 @@ require (
|
||||||
gopkg.in/yaml.v2 v2.3.0
|
gopkg.in/yaml.v2 v2.3.0
|
||||||
maunium.net/go/mauflag v1.0.0
|
maunium.net/go/mauflag v1.0.0
|
||||||
maunium.net/go/maulogger/v2 v2.1.1
|
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
|
replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.3.7
|
||||||
|
|
2
go.sum
2
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-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 h1:9Wxs5S4Wl4S99dbBwfLZYAe/sP7VKaFikw9Ocf88kfk=
|
||||||
maunium.net/go/mautrix v0.7.0/go.mod h1:Va/74MijqaS0DQ3aUqxmFO54/PMfr1LVsCOcGRHbYmo=
|
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=
|
||||||
|
|
|
@ -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)
|
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)
|
err = portal.FillInitialHistory(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
16
puppet.go
16
puppet.go
|
@ -124,18 +124,22 @@ func (bridge *Bridge) dbPuppetsToPuppets(dbPuppets []*database.Puppet) []*Puppet
|
||||||
return output
|
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 {
|
func (bridge *Bridge) NewPuppet(dbPuppet *database.Puppet) *Puppet {
|
||||||
return &Puppet{
|
return &Puppet{
|
||||||
Puppet: dbPuppet,
|
Puppet: dbPuppet,
|
||||||
bridge: bridge,
|
bridge: bridge,
|
||||||
log: bridge.Log.Sub(fmt.Sprintf("Puppet/%s", dbPuppet.JID)),
|
log: bridge.Log.Sub(fmt.Sprintf("Puppet/%s", dbPuppet.JID)),
|
||||||
|
|
||||||
MXID: id.NewUserID(
|
MXID: bridge.FormatPuppetMXID(dbPuppet.JID),
|
||||||
bridge.Config.Bridge.FormatUsername(
|
|
||||||
strings.Replace(
|
|
||||||
dbPuppet.JID,
|
|
||||||
whatsappExt.NewUserSuffix, "", 1)),
|
|
||||||
bridge.Config.Homeserver.Domain),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
59
user.go
59
user.go
|
@ -19,6 +19,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -28,12 +29,12 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/skip2/go-qrcode"
|
"github.com/skip2/go-qrcode"
|
||||||
log "maunium.net/go/maulogger/v2"
|
log "maunium.net/go/maulogger/v2"
|
||||||
"maunium.net/go/mautrix"
|
|
||||||
|
|
||||||
"github.com/Rhymen/go-whatsapp"
|
"github.com/Rhymen/go-whatsapp"
|
||||||
waBinary "github.com/Rhymen/go-whatsapp/binary"
|
waBinary "github.com/Rhymen/go-whatsapp/binary"
|
||||||
waProto "github.com/Rhymen/go-whatsapp/binary/proto"
|
waProto "github.com/Rhymen/go-whatsapp/binary/proto"
|
||||||
|
|
||||||
|
"maunium.net/go/mautrix"
|
||||||
"maunium.net/go/mautrix/event"
|
"maunium.net/go/mautrix/event"
|
||||||
"maunium.net/go/mautrix/format"
|
"maunium.net/go/mautrix/format"
|
||||||
"maunium.net/go/mautrix/id"
|
"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")
|
user.log.Infoln("Finished syncing portals")
|
||||||
select {
|
select {
|
||||||
case user.syncPortalsDone <- struct{}{}:
|
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) {
|
func (user *User) HandleContactList(contacts []whatsapp.Contact) {
|
||||||
contactMap := make(map[string]whatsapp.Contact)
|
contactMap := make(map[string]whatsapp.Contact)
|
||||||
for _, contact := range contacts {
|
for _, contact := range contacts {
|
||||||
|
|
Loading…
Reference in a new issue