|
@@ -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,10 +441,9 @@ func (user *User) Login(ce *CommandEvent) {
|
|
|
}
|
|
|
|
|
|
type Chat struct {
|
|
|
- Portal *Portal
|
|
|
- LastMessageTime uint64
|
|
|
- MarkAsRead bool
|
|
|
- Contact whatsapp.Contact
|
|
|
+ whatsapp.Chat
|
|
|
+ Portal *Portal
|
|
|
+ Contact whatsapp.Contact
|
|
|
}
|
|
|
|
|
|
type ChatList []Chat
|
|
@@ -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{
|
|
|
- Portal: portal,
|
|
|
- Contact: user.Conn.Store.Contacts[chat.JID],
|
|
|
- LastMessageTime: ts,
|
|
|
- MarkAsRead: chat.Unread == "0",
|
|
|
+ Chat: chat,
|
|
|
+ Portal: portal,
|
|
|
+ Contact: user.Conn.Store.Contacts[chat.JID],
|
|
|
})
|
|
|
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()
|
|
|
}
|
|
|
}
|