2018-08-23 00:12:26 +02:00
// mautrix-whatsapp - A Matrix-WhatsApp puppeting bridge.
2021-10-22 19:14:34 +02:00
// Copyright (C) 2021 Tulir Asokan
2018-08-23 00:12:26 +02:00
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package database
import (
2018-08-26 21:53:13 +02:00
"database/sql"
2021-11-03 20:34:06 +01:00
"errors"
2021-02-09 22:41:14 +01:00
"strings"
2021-05-18 19:09:12 +02:00
"time"
2018-08-26 21:53:13 +02:00
2023-08-05 17:59:17 +02:00
"go.mau.fi/util/dbutil"
"go.mau.fi/whatsmeow/types"
2019-01-11 20:17:31 +01:00
log "maunium.net/go/maulogger/v2"
2021-10-26 16:01:10 +02:00
2020-05-08 21:32:22 +02:00
"maunium.net/go/mautrix/id"
2018-08-23 00:12:26 +02:00
)
type MessageQuery struct {
db * Database
log log . Logger
}
func ( mq * MessageQuery ) New ( ) * Message {
return & Message {
db : mq . db ,
log : mq . log ,
}
}
2021-10-27 17:31:33 +02:00
const (
getAllMessagesQuery = `
2023-05-26 14:55:53 +02:00
SELECT chat_jid , chat_receiver , jid , mxid , sender , sender_mxid , timestamp , sent , type , error , broadcast_list_jid FROM message
2021-10-27 17:31:33 +02:00
WHERE chat_jid = $ 1 AND chat_receiver = $ 2
`
getMessageByJIDQuery = `
2023-05-26 14:55:53 +02:00
SELECT chat_jid , chat_receiver , jid , mxid , sender , sender_mxid , timestamp , sent , type , error , broadcast_list_jid FROM message
2021-10-27 17:31:33 +02:00
WHERE chat_jid = $ 1 AND chat_receiver = $ 2 AND jid = $ 3
`
getMessageByMXIDQuery = `
2023-05-26 14:55:53 +02:00
SELECT chat_jid , chat_receiver , jid , mxid , sender , sender_mxid , timestamp , sent , type , error , broadcast_list_jid FROM message
2021-10-27 17:31:33 +02:00
WHERE mxid = $ 1
`
getLastMessageInChatQuery = `
2023-05-26 14:55:53 +02:00
SELECT chat_jid , chat_receiver , jid , mxid , sender , sender_mxid , timestamp , sent , type , error , broadcast_list_jid FROM message
2021-10-27 17:31:33 +02:00
WHERE chat_jid = $ 1 AND chat_receiver = $ 2 AND timestamp <= $ 3 AND sent = true ORDER BY timestamp DESC LIMIT 1
`
getFirstMessageInChatQuery = `
2023-05-26 14:55:53 +02:00
SELECT chat_jid , chat_receiver , jid , mxid , sender , sender_mxid , timestamp , sent , type , error , broadcast_list_jid FROM message
2021-10-27 17:31:33 +02:00
WHERE chat_jid = $ 1 AND chat_receiver = $ 2 AND sent = true ORDER BY timestamp ASC LIMIT 1
`
2021-11-30 15:38:37 +01:00
getMessagesBetweenQuery = `
2023-05-26 14:55:53 +02:00
SELECT chat_jid , chat_receiver , jid , mxid , sender , sender_mxid , timestamp , sent , type , error , broadcast_list_jid FROM message
2022-05-18 00:00:23 +02:00
WHERE chat_jid = $ 1 AND chat_receiver = $ 2 AND timestamp > $ 3 AND timestamp <= $ 4 AND sent = true AND error = ' ' ORDER BY timestamp ASC
2021-11-30 15:38:37 +01:00
`
2021-10-27 17:31:33 +02:00
)
2018-08-28 23:40:54 +02:00
func ( mq * MessageQuery ) GetAll ( chat PortalKey ) ( messages [ ] * Message ) {
2021-10-27 17:31:33 +02:00
rows , err := mq . db . Query ( getAllMessagesQuery , chat . JID , chat . Receiver )
2018-08-23 00:12:26 +02:00
if err != nil || rows == nil {
return nil
}
for rows . Next ( ) {
messages = append ( messages , mq . New ( ) . Scan ( rows ) )
}
return
}
2021-10-22 19:14:34 +02:00
func ( mq * MessageQuery ) GetByJID ( chat PortalKey , jid types . MessageID ) * Message {
2021-10-27 17:31:33 +02:00
return mq . maybeScan ( mq . db . QueryRow ( getMessageByJIDQuery , chat . JID , chat . Receiver , jid ) )
2018-08-23 00:12:26 +02:00
}
2020-05-08 21:32:22 +02:00
func ( mq * MessageQuery ) GetByMXID ( mxid id . EventID ) * Message {
2021-10-27 17:31:33 +02:00
return mq . maybeScan ( mq . db . QueryRow ( getMessageByMXIDQuery , mxid ) )
2019-05-22 15:46:18 +02:00
}
func ( mq * MessageQuery ) GetLastInChat ( chat PortalKey ) * Message {
2021-10-27 17:31:33 +02:00
return mq . GetLastInChatBefore ( chat , time . Now ( ) . Add ( 60 * time . Second ) )
2021-05-18 19:09:12 +02:00
}
2021-10-26 16:01:10 +02:00
func ( mq * MessageQuery ) GetLastInChatBefore ( chat PortalKey , maxTimestamp time . Time ) * Message {
2021-10-27 17:31:33 +02:00
msg := mq . maybeScan ( mq . db . QueryRow ( getLastMessageInChatQuery , chat . JID , chat . Receiver , maxTimestamp . Unix ( ) ) )
2021-10-26 16:01:10 +02:00
if msg == nil || msg . Timestamp . IsZero ( ) {
2019-05-22 15:46:18 +02:00
// Old db, we don't know what the last message is.
return nil
}
return msg
2018-08-23 00:12:26 +02:00
}
2021-10-26 16:01:10 +02:00
func ( mq * MessageQuery ) GetFirstInChat ( chat PortalKey ) * Message {
2021-10-27 17:31:33 +02:00
return mq . maybeScan ( mq . db . QueryRow ( getFirstMessageInChatQuery , chat . JID , chat . Receiver ) )
2021-10-26 16:01:10 +02:00
}
2021-11-30 15:38:37 +01:00
func ( mq * MessageQuery ) GetMessagesBetween ( chat PortalKey , minTimestamp , maxTimestamp time . Time ) ( messages [ ] * Message ) {
rows , err := mq . db . Query ( getMessagesBetweenQuery , chat . JID , chat . Receiver , minTimestamp . Unix ( ) , maxTimestamp . Unix ( ) )
if err != nil || rows == nil {
return nil
}
for rows . Next ( ) {
messages = append ( messages , mq . New ( ) . Scan ( rows ) )
}
return
}
2021-10-27 17:31:33 +02:00
func ( mq * MessageQuery ) maybeScan ( row * sql . Row ) * Message {
2018-08-23 00:12:26 +02:00
if row == nil {
return nil
}
return mq . New ( ) . Scan ( row )
}
2022-02-10 18:18:49 +01:00
type MessageErrorType string
const (
MsgNoError MessageErrorType = ""
MsgErrDecryptionFailed MessageErrorType = "decryption_failed"
MsgErrMediaNotFound MessageErrorType = "media_not_found"
)
2022-03-05 20:22:31 +01:00
type MessageType string
const (
2022-12-23 14:17:57 +01:00
MsgUnknown MessageType = ""
MsgFake MessageType = "fake"
MsgNormal MessageType = "message"
MsgReaction MessageType = "reaction"
MsgEdit MessageType = "edit"
MsgMatrixPoll MessageType = "matrix-poll"
2022-03-05 20:22:31 +01:00
)
2018-08-23 00:12:26 +02:00
type Message struct {
db * Database
log log . Logger
2023-05-26 14:55:53 +02:00
Chat PortalKey
JID types . MessageID
MXID id . EventID
Sender types . JID
SenderMXID id . UserID
Timestamp time . Time
Sent bool
Type MessageType
Error MessageErrorType
2021-10-27 17:31:33 +02:00
2021-12-25 19:50:36 +01:00
BroadcastListJID types . JID
2018-08-23 00:12:26 +02:00
}
2021-02-09 22:41:14 +01:00
func ( msg * Message ) IsFakeMXID ( ) bool {
return strings . HasPrefix ( msg . MXID . String ( ) , "net.maunium.whatsapp.fake::" )
}
2021-11-02 14:46:31 +01:00
func ( msg * Message ) IsFakeJID ( ) bool {
2022-02-15 23:11:38 +01:00
return strings . HasPrefix ( msg . JID , "FAKE::" ) || msg . JID == string ( msg . MXID )
2021-11-02 14:46:31 +01:00
}
2022-05-22 00:06:30 +02:00
func ( msg * Message ) Scan ( row dbutil . Scannable ) * Message {
2021-10-26 16:01:10 +02:00
var ts int64
2023-05-26 14:55:53 +02:00
err := row . Scan ( & msg . Chat . JID , & msg . Chat . Receiver , & msg . JID , & msg . MXID , & msg . Sender , & msg . SenderMXID , & ts , & msg . Sent , & msg . Type , & msg . Error , & msg . BroadcastListJID )
2018-08-23 00:12:26 +02:00
if err != nil {
2021-11-03 20:34:06 +01:00
if ! errors . Is ( err , sql . ErrNoRows ) {
2018-08-26 00:55:21 +02:00
msg . log . Errorln ( "Database scan failed:" , err )
2018-08-23 00:12:26 +02:00
}
return nil
}
2021-10-26 16:01:10 +02:00
if ts != 0 {
msg . Timestamp = time . Unix ( ts , 0 )
}
2018-08-23 00:12:26 +02:00
return msg
}
2022-11-10 22:08:29 +01:00
func ( msg * Message ) Insert ( txn dbutil . Execable ) {
if txn == nil {
txn = msg . db
}
2021-10-26 20:30:42 +02:00
var sender interface { } = msg . Sender
// Slightly hacky hack to allow inserting empty senders (used for post-backfill dummy events)
if msg . Sender . IsEmpty ( ) {
sender = ""
}
2022-11-10 22:08:29 +01:00
_ , err := txn . Exec ( `
2022-05-13 01:56:40 +02:00
INSERT INTO message
2023-05-26 14:55:53 +02:00
( chat_jid , chat_receiver , jid , mxid , sender , sender_mxid , timestamp , sent , type , error , broadcast_list_jid )
VALUES ( $ 1 , $ 2 , $ 3 , $ 4 , $ 5 , $ 6 , $ 7 , $ 8 , $ 9 , $ 10 , $ 11 )
` , msg . Chat . JID , msg . Chat . Receiver , msg . JID , msg . MXID , sender , msg . SenderMXID , msg . Timestamp . Unix ( ) , msg . Sent , msg . Type , msg . Error , msg . BroadcastListJID )
2018-08-23 00:12:26 +02:00
if err != nil {
2018-09-01 22:38:03 +02:00
msg . log . Warnfln ( "Failed to insert %s@%s: %v" , msg . Chat , msg . JID , err )
2018-08-23 00:12:26 +02:00
}
}
2019-05-16 00:59:36 +02:00
2021-10-31 22:36:41 +01:00
func ( msg * Message ) MarkSent ( ts time . Time ) {
2021-02-17 00:22:06 +01:00
msg . Sent = true
2021-10-31 22:36:41 +01:00
msg . Timestamp = ts
2021-11-07 22:42:23 +01:00
_ , err := msg . db . Exec ( "UPDATE message SET sent=true, timestamp=$1 WHERE chat_jid=$2 AND chat_receiver=$3 AND jid=$4" , ts . Unix ( ) , msg . Chat . JID , msg . Chat . Receiver , msg . JID )
2021-02-17 00:22:06 +01:00
if err != nil {
msg . log . Warnfln ( "Failed to update %s@%s: %v" , msg . Chat , msg . JID , err )
}
}
2022-11-10 22:08:29 +01:00
func ( msg * Message ) UpdateMXID ( txn dbutil . Execable , mxid id . EventID , newType MessageType , newError MessageErrorType ) {
if txn == nil {
txn = msg . db
}
2021-10-27 17:31:33 +02:00
msg . MXID = mxid
2022-03-05 20:22:31 +01:00
msg . Type = newType
2022-02-10 18:18:49 +01:00
msg . Error = newError
2022-11-10 22:08:29 +01:00
_ , err := txn . Exec ( "UPDATE message SET mxid=$1, type=$2, error=$3 WHERE chat_jid=$4 AND chat_receiver=$5 AND jid=$6" ,
mxid , newType , newError , msg . Chat . JID , msg . Chat . Receiver , msg . JID )
2021-10-27 17:31:33 +02:00
if err != nil {
msg . log . Warnfln ( "Failed to update %s@%s: %v" , msg . Chat , msg . JID , err )
}
}
2019-05-16 00:59:36 +02:00
func ( msg * Message ) Delete ( ) {
_ , err := msg . db . Exec ( "DELETE FROM message WHERE chat_jid=$1 AND chat_receiver=$2 AND jid=$3" , msg . Chat . JID , msg . Chat . Receiver , msg . JID )
if err != nil {
msg . log . Warnfln ( "Failed to delete %s@%s: %v" , msg . Chat , msg . JID , err )
}
}