forked from MirrorHub/mautrix-whatsapp
Add option to bridge archive and mute status from WhatsApp
This commit is contained in:
parent
06a041981d
commit
badea9c547
8 changed files with 139 additions and 55 deletions
|
@ -59,7 +59,7 @@ type BridgeConfig struct {
|
|||
ChatMetaSync bool `yaml:"chat_meta_sync"`
|
||||
UserAvatarSync bool `yaml:"user_avatar_sync"`
|
||||
BridgeMatrixLeave bool `yaml:"bridge_matrix_leave"`
|
||||
SyncChatMaxAge uint64 `yaml:"sync_max_chat_age"`
|
||||
SyncChatMaxAge int64 `yaml:"sync_max_chat_age"`
|
||||
|
||||
SyncWithCustomPuppets bool `yaml:"sync_with_custom_puppets"`
|
||||
SyncDirectChatList bool `yaml:"sync_direct_chat_list"`
|
||||
|
@ -71,6 +71,8 @@ type BridgeConfig struct {
|
|||
PrivateChatPortalMeta bool `yaml:"private_chat_portal_meta"`
|
||||
BridgeNotices bool `yaml:"bridge_notices"`
|
||||
ResendBridgeInfo bool `yaml:"resend_bridge_info"`
|
||||
MuteBridging bool `yaml:"mute_bridging"`
|
||||
ArchiveTag string `yaml:"archive_tag"`
|
||||
|
||||
WhatsappThumbnail bool `yaml:"whatsapp_thumbnail"`
|
||||
|
||||
|
|
|
@ -91,7 +91,7 @@ type Message struct {
|
|||
JID whatsapp.MessageID
|
||||
MXID id.EventID
|
||||
Sender whatsapp.JID
|
||||
Timestamp uint64
|
||||
Timestamp int64
|
||||
Sent bool
|
||||
Content *waProto.Message
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ type User struct {
|
|||
JID whatsapp.JID
|
||||
ManagementRoom id.RoomID
|
||||
Session *whatsapp.Session
|
||||
LastConnection uint64
|
||||
LastConnection int64
|
||||
}
|
||||
|
||||
func (user *User) Scan(row Scannable) *User {
|
||||
|
@ -146,7 +146,7 @@ func (user *User) Insert() {
|
|||
}
|
||||
|
||||
func (user *User) UpdateLastConnection() {
|
||||
user.LastConnection = uint64(time.Now().Unix())
|
||||
user.LastConnection = time.Now().Unix()
|
||||
_, err := user.db.Exec(`UPDATE "user" SET last_connection=$1 WHERE mxid=$2`,
|
||||
user.LastConnection, user.MXID)
|
||||
if err != nil {
|
||||
|
|
|
@ -183,6 +183,12 @@ bridge:
|
|||
# This field will automatically be changed back to false after it,
|
||||
# except if the config file is not writable.
|
||||
resend_bridge_info: false
|
||||
# When using double puppeting, should muted chats be muted in Matrix?
|
||||
mute_bridging: false
|
||||
# When using double puppeting, should archived chats be moved to a specific tag in Matrix?
|
||||
# Note that WhatsApp unarchives chats when a message is received, which will also be mirrored to Matrix.
|
||||
# This can be set to a tag (e.g. m.lowpriority), or null to disable.
|
||||
archive_tag: null
|
||||
|
||||
# Whether or not thumbnails from WhatsApp should be sent.
|
||||
# They're disabled by default due to very low resolution.
|
||||
|
|
2
go.mod
2
go.mod
|
@ -16,4 +16,4 @@ require (
|
|||
maunium.net/go/mautrix v0.9.7
|
||||
)
|
||||
|
||||
replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.4.3
|
||||
replace github.com/Rhymen/go-whatsapp => github.com/tulir/go-whatsapp v0.4.4
|
||||
|
|
2
go.sum
2
go.sum
|
@ -323,6 +323,8 @@ github.com/tulir/go-whatsapp v0.4.2 h1:UzBidzRazkbFhM7xyDBLvv4eD37zEtOsVLWK0m2CI
|
|||
github.com/tulir/go-whatsapp v0.4.2/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E=
|
||||
github.com/tulir/go-whatsapp v0.4.3 h1:rQBBT40JHE4eLk5idQ3r/6jNj46nqjLyMnlJTKwyHl0=
|
||||
github.com/tulir/go-whatsapp v0.4.3/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E=
|
||||
github.com/tulir/go-whatsapp v0.4.4 h1:69AIE/CbmVYpBbug75meWFOS8lilzoafZFctt2JzRek=
|
||||
github.com/tulir/go-whatsapp v0.4.4/go.mod h1:rwwuTh1bKqhgrRvOBAr8hDqtuz8Cc1Quqw/0BeXb+/E=
|
||||
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/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
|
|
|
@ -308,7 +308,7 @@ func (portal *Portal) markHandled(source *User, message *waProto.WebMessageInfo,
|
|||
msg.Chat = portal.Key
|
||||
msg.JID = message.GetKey().GetId()
|
||||
msg.MXID = mxid
|
||||
msg.Timestamp = message.GetMessageTimestamp()
|
||||
msg.Timestamp = int64(message.GetMessageTimestamp())
|
||||
if message.GetKey().GetFromMe() {
|
||||
msg.Sender = source.JID
|
||||
} else if portal.IsPrivateChat() {
|
||||
|
@ -765,7 +765,7 @@ func (portal *Portal) RestrictMetadataChanges(restrict bool) id.EventID {
|
|||
return ""
|
||||
}
|
||||
|
||||
func (portal *Portal) BackfillHistory(user *User, lastMessageTime uint64) error {
|
||||
func (portal *Portal) BackfillHistory(user *User, lastMessageTime int64) error {
|
||||
if !portal.bridge.Config.Bridge.RecoverHistory {
|
||||
return nil
|
||||
}
|
||||
|
|
138
user.go
138
user.go
|
@ -23,7 +23,6 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -31,6 +30,8 @@ import (
|
|||
|
||||
"github.com/skip2/go-qrcode"
|
||||
log "maunium.net/go/maulogger/v2"
|
||||
"maunium.net/go/mautrix/appservice"
|
||||
"maunium.net/go/mautrix/pushrules"
|
||||
|
||||
"github.com/Rhymen/go-whatsapp"
|
||||
waBinary "github.com/Rhymen/go-whatsapp/binary"
|
||||
|
@ -440,9 +441,8 @@ func (user *User) Login(ce *CommandEvent) {
|
|||
}
|
||||
|
||||
type Chat struct {
|
||||
whatsapp.Chat
|
||||
Portal *Portal
|
||||
LastMessageTime uint64
|
||||
MarkAsRead bool
|
||||
Contact whatsapp.Contact
|
||||
}
|
||||
|
||||
|
@ -618,6 +618,16 @@ func (user *User) HandleEvent(event interface{}) {
|
|||
user.HandleChatUpdate(v)
|
||||
case whatsapp.ConnInfo:
|
||||
user.HandleConnInfo(v)
|
||||
case whatsapp.MuteMessage:
|
||||
portal := user.bridge.GetPortalByJID(user.PortalKey(v.JID))
|
||||
if portal != nil {
|
||||
go user.updateChatMute(nil, portal, v.MutedUntil)
|
||||
}
|
||||
case whatsapp.ArchiveMessage:
|
||||
portal := user.bridge.GetPortalByJID(user.PortalKey(v.JID))
|
||||
if portal != nil {
|
||||
go user.updateChatArchive(nil, portal, v.IsArchived)
|
||||
}
|
||||
case json.RawMessage:
|
||||
user.HandleJSONMessage(v)
|
||||
case *waProto.WebMessageInfo:
|
||||
|
@ -664,9 +674,84 @@ func (user *User) HandleChatList(chats []whatsapp.Chat) {
|
|||
go user.syncPortals(chatMap, false)
|
||||
}
|
||||
|
||||
func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool) {
|
||||
// TODO use contexts instead of checking if user.Conn is the same?
|
||||
connAtStart := user.Conn
|
||||
func (user *User) updateChatMute(intent *appservice.IntentAPI, portal *Portal, mutedUntil int64) {
|
||||
if len(portal.MXID) == 0 || !user.bridge.Config.Bridge.MuteBridging {
|
||||
return
|
||||
} else if intent == nil {
|
||||
doublePuppet := user.bridge.GetPuppetByCustomMXID(user.MXID)
|
||||
if doublePuppet == nil || doublePuppet.CustomIntent() == nil {
|
||||
return
|
||||
}
|
||||
intent = doublePuppet.CustomIntent()
|
||||
}
|
||||
var err error
|
||||
if mutedUntil < time.Now().Unix() {
|
||||
user.log.Debugln("Unmuting", portal.MXID)
|
||||
err = intent.DeletePushRule("global", pushrules.RoomRule, string(portal.MXID))
|
||||
} else {
|
||||
user.log.Debugln("Muting", portal.MXID)
|
||||
err = intent.PutPushRule("global", pushrules.RoomRule, string(portal.MXID), &mautrix.ReqPutPushRule{
|
||||
Actions: []pushrules.PushActionType{pushrules.ActionDontNotify},
|
||||
})
|
||||
}
|
||||
if err != nil && !errors.Is(err, mautrix.MNotFound) {
|
||||
user.log.Warnfln("Failed to update push rule for %s through double puppet: %v", portal.MXID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) updateChatArchive(intent *appservice.IntentAPI, portal *Portal, archived bool) {
|
||||
if len(portal.MXID) == 0 || len(user.bridge.Config.Bridge.ArchiveTag) == 0 {
|
||||
return
|
||||
} else if intent == nil {
|
||||
doublePuppet := user.bridge.GetPuppetByCustomMXID(user.MXID)
|
||||
if doublePuppet == nil || doublePuppet.CustomIntent() == nil {
|
||||
return
|
||||
}
|
||||
intent = doublePuppet.CustomIntent()
|
||||
}
|
||||
var err error
|
||||
if archived {
|
||||
user.log.Debugln("Adding tag", user.bridge.Config.Bridge.ArchiveTag, "to", portal.MXID)
|
||||
err = intent.AddTag(portal.MXID, user.bridge.Config.Bridge.ArchiveTag, 0.5)
|
||||
} else {
|
||||
user.log.Debugln("Removing tag", user.bridge.Config.Bridge.ArchiveTag, "from", portal.MXID)
|
||||
err = intent.RemoveTag(portal.MXID, user.bridge.Config.Bridge.ArchiveTag)
|
||||
}
|
||||
if err != nil {
|
||||
user.log.Warnfln("Failed to update tag for %s through double puppet: %v", portal.MXID, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) syncChatDoublePuppetDetails(doublePuppet *Puppet, chat Chat) {
|
||||
if doublePuppet == nil || doublePuppet.CustomIntent() == nil {
|
||||
return
|
||||
}
|
||||
intent := doublePuppet.CustomIntent()
|
||||
if chat.UnreadCount == 0 {
|
||||
lastMessage := user.bridge.DB.Message.GetLastInChat(chat.Portal.Key)
|
||||
if lastMessage != nil {
|
||||
err := intent.MarkRead(chat.Portal.MXID, lastMessage.MXID)
|
||||
if err != nil {
|
||||
user.log.Warnln("Failed to mark %s in %s as read after backfill: %v", lastMessage.MXID, chat.Portal.MXID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
user.updateChatMute(intent, chat.Portal, chat.MutedUntil)
|
||||
user.updateChatArchive(intent, chat.Portal, chat.IsArchived)
|
||||
}
|
||||
|
||||
func (user *User) syncPortal(chat Chat) {
|
||||
// Don't sync unless chat meta sync is enabled or portal doesn't exist
|
||||
if user.bridge.Config.Bridge.ChatMetaSync || len(chat.Portal.MXID) == 0 {
|
||||
chat.Portal.Sync(user, chat.Contact)
|
||||
}
|
||||
err := chat.Portal.BackfillHistory(user, chat.LastMessageTime)
|
||||
if err != nil {
|
||||
chat.Portal.log.Errorln("Error backfilling history:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (user *User) collectChatList(chatMap map[string]whatsapp.Chat) ChatList {
|
||||
if chatMap == nil {
|
||||
chatMap = user.Conn.Store.Chats
|
||||
}
|
||||
|
@ -675,18 +760,12 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
|
|||
existingKeys := user.GetInCommunityMap()
|
||||
portalKeys := make([]database.PortalKeyWithMeta, 0, len(chatMap))
|
||||
for _, chat := range chatMap {
|
||||
ts, err := strconv.ParseUint(chat.LastMessageTime, 10, 64)
|
||||
if err != nil {
|
||||
user.log.Warnfln("Non-integer last message time in %s: %s", chat.JID, chat.LastMessageTime)
|
||||
continue
|
||||
}
|
||||
portal := user.GetPortalByJID(chat.JID)
|
||||
|
||||
chats = append(chats, Chat{
|
||||
Chat: chat,
|
||||
Portal: portal,
|
||||
Contact: user.Conn.Store.Contacts[chat.JID],
|
||||
LastMessageTime: ts,
|
||||
MarkAsRead: chat.Unread == "0",
|
||||
})
|
||||
var inCommunity, ok bool
|
||||
if inCommunity, ok = existingKeys[portal.Key]; !ok || !inCommunity {
|
||||
|
@ -704,6 +783,15 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
|
|||
user.log.Warnln("Failed to update user-portal mapping:", err)
|
||||
}
|
||||
sort.Sort(chats)
|
||||
return chats
|
||||
}
|
||||
|
||||
func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool) {
|
||||
// TODO use contexts instead of checking if user.Conn is the same?
|
||||
connAtStart := user.Conn
|
||||
|
||||
chats := user.collectChatList(chatMap)
|
||||
|
||||
limit := user.bridge.Config.Bridge.InitialChatSync
|
||||
if limit < 0 {
|
||||
limit = len(chats)
|
||||
|
@ -712,7 +800,7 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
|
|||
user.log.Debugln("Connection seems to have changed before sync, cancelling")
|
||||
return
|
||||
}
|
||||
now := uint64(time.Now().Unix())
|
||||
now := time.Now().Unix()
|
||||
user.log.Infoln("Syncing portals")
|
||||
doublePuppet := user.bridge.GetPuppetByCustomMXID(user.MXID)
|
||||
for i, chat := range chats {
|
||||
|
@ -721,23 +809,8 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
|
|||
}
|
||||
create := (chat.LastMessageTime >= user.LastConnection && user.LastConnection > 0) || i < limit
|
||||
if len(chat.Portal.MXID) > 0 || create || createAll {
|
||||
// Don't sync unless chat meta sync is enabled or portal doesn't exist
|
||||
if user.bridge.Config.Bridge.ChatMetaSync || len(chat.Portal.MXID) == 0 {
|
||||
chat.Portal.Sync(user, chat.Contact)
|
||||
}
|
||||
err = chat.Portal.BackfillHistory(user, chat.LastMessageTime)
|
||||
if err != nil {
|
||||
chat.Portal.log.Errorln("Error backfilling history:", err)
|
||||
}
|
||||
if chat.MarkAsRead && doublePuppet != nil && doublePuppet.CustomIntent() != nil {
|
||||
lastMessage := user.bridge.DB.Message.GetLastInChat(chat.Portal.Key)
|
||||
if lastMessage != nil {
|
||||
err = doublePuppet.CustomIntent().MarkRead(chat.Portal.MXID, lastMessage.MXID)
|
||||
if err != nil {
|
||||
user.log.Warnln("Failed to mark %s in %s as read after backfill: %v", lastMessage.MXID, chat.Portal.MXID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
user.syncPortal(chat)
|
||||
user.syncChatDoublePuppetDetails(doublePuppet, chat)
|
||||
}
|
||||
}
|
||||
if user.Conn != connAtStart {
|
||||
|
@ -745,6 +818,7 @@ func (user *User) syncPortals(chatMap map[string]whatsapp.Chat, createAll bool)
|
|||
return
|
||||
}
|
||||
user.UpdateDirectChats(nil)
|
||||
|
||||
user.log.Infoln("Finished syncing portals")
|
||||
select {
|
||||
case user.syncPortalsDone <- struct{}{}:
|
||||
|
@ -846,7 +920,7 @@ func (user *User) syncPuppets(contacts map[whatsapp.JID]whatsapp.Contact) {
|
|||
}
|
||||
|
||||
func (user *User) updateLastConnectionIfNecessary() {
|
||||
if user.LastConnection+60 < uint64(time.Now().Unix()) {
|
||||
if user.LastConnection+60 < time.Now().Unix() {
|
||||
user.UpdateLastConnection()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue