mirror of
https://github.com/mautrix/whatsapp.git
synced 2026-05-15 18:16:53 -04:00
204 lines
7.2 KiB
Go
204 lines
7.2 KiB
Go
package wadb
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"time"
|
|
|
|
"go.mau.fi/util/dbutil"
|
|
"go.mau.fi/util/jsontime"
|
|
"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
|
|
Archived *bool
|
|
Pinned *bool
|
|
MuteEndTime time.Time
|
|
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 {
|
|
var pinned *bool
|
|
if conv.Pinned != nil {
|
|
pinned = ptr.Ptr(*conv.Pinned > 0)
|
|
}
|
|
lastMessageTS := parseHistoryTime(conv.LastMsgTimestamp)
|
|
if lastMessageTS.IsZero() {
|
|
lastMessageTS = mostRecentMessage
|
|
}
|
|
return &Conversation{
|
|
UserLoginID: loginID,
|
|
ChatJID: chatJID,
|
|
LastMessageTimestamp: lastMessageTS,
|
|
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
|
|
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,
|
|
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),
|
|
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 {
|
|
var lastMessageTS, muteEndTime *int64
|
|
if !c.LastMessageTimestamp.IsZero() {
|
|
lastMessageTS = ptr.Ptr(c.LastMessageTimestamp.Unix())
|
|
}
|
|
if !c.MuteEndTime.IsZero() {
|
|
muteEndTime = ptr.Ptr(c.MuteEndTime.Unix())
|
|
}
|
|
return []any{
|
|
c.BridgeID,
|
|
c.UserLoginID,
|
|
c.ChatJID,
|
|
lastMessageTS,
|
|
c.Archived,
|
|
c.Pinned,
|
|
muteEndTime,
|
|
c.EndOfHistoryTransferType,
|
|
c.EphemeralExpiration,
|
|
c.EphemeralSettingTimestamp,
|
|
c.MarkedAsUnread,
|
|
c.UnreadCount,
|
|
}
|
|
}
|
|
|
|
func (c *Conversation) Scan(row dbutil.Scannable) (*Conversation, error) {
|
|
var lastMessageTS, muteEndTime sql.NullInt64
|
|
err := row.Scan(
|
|
&c.BridgeID,
|
|
&c.UserLoginID,
|
|
&c.ChatJID,
|
|
&lastMessageTS,
|
|
&c.Archived,
|
|
&c.Pinned,
|
|
&muteEndTime,
|
|
&c.EndOfHistoryTransferType,
|
|
&c.EphemeralExpiration,
|
|
&c.EphemeralSettingTimestamp,
|
|
&c.MarkedAsUnread,
|
|
&c.UnreadCount,
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if lastMessageTS.Int64 != 0 {
|
|
c.LastMessageTimestamp = time.Unix(lastMessageTS.Int64, 0)
|
|
}
|
|
if muteEndTime.Int64 != 0 {
|
|
c.MuteEndTime = time.Unix(muteEndTime.Int64, 0)
|
|
}
|
|
return c, nil
|
|
}
|