1
0
Fork 0
mirror of https://github.com/mautrix/whatsapp.git synced 2026-05-16 02:26:53 -04:00
mautrix-whatsapp/pkg/connector/wadb/conversation.go

204 lines
7.2 KiB
Go
Raw Permalink Normal View History

package wadb
import (
"context"
2024-10-01 16:31:19 +03:00
"database/sql"
"time"
"go.mau.fi/util/dbutil"
"go.mau.fi/util/jsontime"
2024-09-17 17:37:13 +03:00
"go.mau.fi/util/ptr"
"go.mau.fi/whatsmeow/proto/waHistorySync"
"go.mau.fi/whatsmeow/types"
"maunium.net/go/mautrix/bridgev2/networkid"
)
type ConversationQuery struct {
BridgeID networkid.BridgeID
*dbutil.QueryHelper[*Conversation]
}
type Conversation struct {
BridgeID networkid.BridgeID
UserLoginID networkid.UserLoginID
ChatJID types.JID
LastMessageTimestamp time.Time
2024-09-17 17:37:13 +03:00
Archived *bool
Pinned *bool
MuteEndTime time.Time
2024-09-17 17:37:13 +03:00
EndOfHistoryTransferType *waHistorySync.Conversation_EndOfHistoryTransferType
EphemeralExpiration *uint32
EphemeralSettingTimestamp *int64
MarkedAsUnread *bool
UnreadCount *uint32
}
func parseHistoryTime(ts *uint64) time.Time {
if ts == nil || *ts == 0 {
return time.Time{}
}
return time.Unix(int64(*ts), 0)
}
func NewConversation(loginID networkid.UserLoginID, chatJID types.JID, conv *waHistorySync.Conversation, mostRecentMessage time.Time) *Conversation {
2024-09-17 17:37:13 +03:00
var pinned *bool
if conv.Pinned != nil {
pinned = ptr.Ptr(*conv.Pinned > 0)
}
lastMessageTS := parseHistoryTime(conv.LastMsgTimestamp)
if lastMessageTS.IsZero() {
lastMessageTS = mostRecentMessage
}
2024-09-17 17:37:13 +03:00
return &Conversation{
UserLoginID: loginID,
ChatJID: chatJID,
LastMessageTimestamp: lastMessageTS,
2024-09-17 17:37:13 +03:00
Archived: conv.Archived,
Pinned: pinned,
MuteEndTime: parseHistoryTime(conv.MuteEndTime),
EndOfHistoryTransferType: conv.EndOfHistoryTransferType,
EphemeralExpiration: conv.EphemeralExpiration,
EphemeralSettingTimestamp: conv.EphemeralSettingTimestamp,
MarkedAsUnread: conv.MarkedAsUnread,
UnreadCount: conv.UnreadCount,
}
}
const (
upsertHistorySyncConversationQuery = `
INSERT INTO whatsapp_history_sync_conversation (
bridge_id, user_login_id, chat_jid, last_message_timestamp, archived, pinned, mute_end_time,
end_of_history_transfer_type, ephemeral_expiration, ephemeral_setting_timestamp, marked_as_unread,
unread_count
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
ON CONFLICT (bridge_id, user_login_id, chat_jid)
DO UPDATE SET
last_message_timestamp=CASE
2024-09-17 17:37:13 +03:00
WHEN whatsapp_history_sync_conversation.last_message_timestamp IS NULL
OR excluded.last_message_timestamp > whatsapp_history_sync_conversation.last_message_timestamp
THEN excluded.last_message_timestamp
ELSE whatsapp_history_sync_conversation.last_message_timestamp
END,
2024-09-17 17:37:13 +03:00
archived=COALESCE(excluded.archived, whatsapp_history_sync_conversation.archived),
pinned=COALESCE(excluded.pinned, whatsapp_history_sync_conversation.pinned),
mute_end_time=COALESCE(excluded.mute_end_time, whatsapp_history_sync_conversation.mute_end_time),
end_of_history_transfer_type=COALESCE(excluded.end_of_history_transfer_type, whatsapp_history_sync_conversation.end_of_history_transfer_type),
ephemeral_expiration=COALESCE(excluded.ephemeral_expiration, whatsapp_history_sync_conversation.ephemeral_expiration),
ephemeral_setting_timestamp=COALESCE(excluded.ephemeral_setting_timestamp, whatsapp_history_sync_conversation.ephemeral_setting_timestamp),
2024-09-17 17:37:13 +03:00
marked_as_unread=COALESCE(excluded.marked_as_unread, whatsapp_history_sync_conversation.marked_as_unread),
unread_count=COALESCE(excluded.unread_count, whatsapp_history_sync_conversation.unread_count)
`
getRecentConversations = `
SELECT
bridge_id, user_login_id, chat_jid, last_message_timestamp, archived, pinned, mute_end_time,
end_of_history_transfer_type, ephemeral_expiration, ephemeral_setting_timestamp, marked_as_unread,
unread_count
FROM whatsapp_history_sync_conversation
WHERE bridge_id=$1 AND user_login_id=$2 AND (synced_login_ts IS NULL OR synced_login_ts < $4)
ORDER BY last_message_timestamp DESC
LIMIT $3
`
getConversationByJID = `
SELECT
bridge_id, user_login_id, chat_jid, last_message_timestamp, archived, pinned, mute_end_time,
end_of_history_transfer_type, ephemeral_expiration, ephemeral_setting_timestamp, marked_as_unread,
unread_count
FROM whatsapp_history_sync_conversation
WHERE bridge_id=$1 AND user_login_id=$2 AND chat_jid=$3
`
deleteAllConversationsQuery = "DELETE FROM whatsapp_history_sync_conversation WHERE bridge_id=$1 AND user_login_id=$2"
deleteConversationQuery = `
DELETE FROM whatsapp_history_sync_conversation
WHERE bridge_id=$1 AND user_login_id=$2 AND chat_jid=$3
`
markConversationSynced = `
UPDATE whatsapp_history_sync_conversation
SET synced_login_ts=$4
WHERE bridge_id=$1 AND user_login_id=$2 AND chat_jid=$3
`
)
func (cq *ConversationQuery) Put(ctx context.Context, conv *Conversation) error {
conv.BridgeID = cq.BridgeID
return cq.Exec(ctx, upsertHistorySyncConversationQuery, conv.sqlVariables()...)
}
func (cq *ConversationQuery) GetRecent(
ctx context.Context, loginID networkid.UserLoginID, limit int, notSyncedAfter jsontime.Unix,
) ([]*Conversation, error) {
limitPtr := &limit
// Negative limit on SQLite means unlimited, but Postgres prefers a NULL limit.
if limit < 0 && cq.GetDB().Dialect == dbutil.Postgres {
limitPtr = nil
}
return cq.QueryMany(ctx, getRecentConversations, cq.BridgeID, loginID, limitPtr, notSyncedAfter)
}
func (cq *ConversationQuery) MarkSynced(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID, loginTS jsontime.Unix) error {
return cq.Exec(ctx, markConversationSynced, cq.BridgeID, loginID, chatJID, loginTS)
}
func (cq *ConversationQuery) Get(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID) (*Conversation, error) {
return cq.QueryOne(ctx, getConversationByJID, cq.BridgeID, loginID, chatJID)
}
func (cq *ConversationQuery) DeleteAll(ctx context.Context, loginID networkid.UserLoginID) error {
return cq.Exec(ctx, deleteAllConversationsQuery, cq.BridgeID, loginID)
}
func (cq *ConversationQuery) Delete(ctx context.Context, loginID networkid.UserLoginID, chatJID types.JID) error {
return cq.Exec(ctx, deleteConversationQuery, cq.BridgeID, loginID, chatJID)
}
func (c *Conversation) sqlVariables() []any {
2024-10-01 16:31:19 +03:00
var lastMessageTS, muteEndTime *int64
2024-09-17 17:37:13 +03:00
if !c.LastMessageTimestamp.IsZero() {
2024-10-01 16:31:19 +03:00
lastMessageTS = ptr.Ptr(c.LastMessageTimestamp.Unix())
2024-09-17 17:37:13 +03:00
}
if !c.MuteEndTime.IsZero() {
2024-10-01 16:31:19 +03:00
muteEndTime = ptr.Ptr(c.MuteEndTime.Unix())
2024-09-17 17:37:13 +03:00
}
return []any{
c.BridgeID,
c.UserLoginID,
c.ChatJID,
2024-09-17 17:37:13 +03:00
lastMessageTS,
c.Archived,
c.Pinned,
2024-09-17 17:37:13 +03:00
muteEndTime,
c.EndOfHistoryTransferType,
2024-09-17 17:37:13 +03:00
c.EphemeralExpiration,
c.EphemeralSettingTimestamp,
c.MarkedAsUnread,
c.UnreadCount,
}
}
func (c *Conversation) Scan(row dbutil.Scannable) (*Conversation, error) {
2024-10-01 16:31:19 +03:00
var lastMessageTS, muteEndTime sql.NullInt64
err := row.Scan(
&c.BridgeID,
&c.UserLoginID,
&c.ChatJID,
&lastMessageTS,
&c.Archived,
&c.Pinned,
&muteEndTime,
&c.EndOfHistoryTransferType,
2024-09-17 17:37:13 +03:00
&c.EphemeralExpiration,
&c.EphemeralSettingTimestamp,
&c.MarkedAsUnread,
&c.UnreadCount,
)
if err != nil {
return nil, err
}
2024-10-01 16:31:19 +03:00
if lastMessageTS.Int64 != 0 {
c.LastMessageTimestamp = time.Unix(lastMessageTS.Int64, 0)
}
2024-10-01 16:31:19 +03:00
if muteEndTime.Int64 != 0 {
c.MuteEndTime = time.Unix(muteEndTime.Int64, 0)
}
return c, nil
}