mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-07-03 14:17:45 +00:00
Add dependencies/vendor (whatsapp)
This commit is contained in:
245
vendor/go.mau.fi/whatsmeow/store/sqlstore/container.go
vendored
Normal file
245
vendor/go.mau.fi/whatsmeow/store/sqlstore/container.go
vendored
Normal file
@ -0,0 +1,245 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
mathRand "math/rand"
|
||||
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
"go.mau.fi/whatsmeow/store"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/util/keys"
|
||||
waLog "go.mau.fi/whatsmeow/util/log"
|
||||
)
|
||||
|
||||
// Container is a wrapper for a SQL database that can contain multiple whatsmeow sessions.
|
||||
type Container struct {
|
||||
db *sql.DB
|
||||
dialect string
|
||||
log waLog.Logger
|
||||
}
|
||||
|
||||
var _ store.DeviceContainer = (*Container)(nil)
|
||||
|
||||
// New connects to the given SQL database and wraps it in a Container.
|
||||
//
|
||||
// Only SQLite and Postgres are currently fully supported.
|
||||
//
|
||||
// The logger can be nil and will default to a no-op logger.
|
||||
//
|
||||
// When using SQLite, it's strongly recommended to enable foreign keys by adding `?_foreign_keys=true`:
|
||||
// container, err := sqlstore.New("sqlite3", "file:yoursqlitefile.db?_foreign_keys=on", nil)
|
||||
func New(dialect, address string, log waLog.Logger) (*Container, error) {
|
||||
db, err := sql.Open(dialect, address)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open database: %w", err)
|
||||
}
|
||||
container := NewWithDB(db, dialect, log)
|
||||
err = container.Upgrade()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to upgrade database: %w", err)
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
||||
// NewWithDB wraps an existing SQL connection in a Container.
|
||||
//
|
||||
// Only SQLite and Postgres are currently fully supported.
|
||||
//
|
||||
// The logger can be nil and will default to a no-op logger.
|
||||
//
|
||||
// When using SQLite, it's strongly recommended to enable foreign keys by adding `?_foreign_keys=true`:
|
||||
// db, err := sql.Open("sqlite3", "file:yoursqlitefile.db?_foreign_keys=on")
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// container, err := sqlstore.NewWithDB(db, "sqlite3", nil)
|
||||
func NewWithDB(db *sql.DB, dialect string, log waLog.Logger) *Container {
|
||||
if log == nil {
|
||||
log = waLog.Noop
|
||||
}
|
||||
return &Container{
|
||||
db: db,
|
||||
dialect: dialect,
|
||||
log: log,
|
||||
}
|
||||
}
|
||||
|
||||
const getAllDevicesQuery = `
|
||||
SELECT jid, registration_id, noise_key, identity_key,
|
||||
signed_pre_key, signed_pre_key_id, signed_pre_key_sig,
|
||||
adv_key, adv_details, adv_account_sig, adv_device_sig,
|
||||
platform, business_name, push_name
|
||||
FROM whatsmeow_device
|
||||
`
|
||||
|
||||
const getDeviceQuery = getAllDevicesQuery + " WHERE jid=$1"
|
||||
|
||||
type scannable interface {
|
||||
Scan(dest ...interface{}) error
|
||||
}
|
||||
|
||||
func (c *Container) scanDevice(row scannable) (*store.Device, error) {
|
||||
var device store.Device
|
||||
device.Log = c.log
|
||||
device.SignedPreKey = &keys.PreKey{}
|
||||
var noisePriv, identityPriv, preKeyPriv, preKeySig []byte
|
||||
var account waProto.ADVSignedDeviceIdentity
|
||||
|
||||
err := row.Scan(
|
||||
&device.ID, &device.RegistrationID, &noisePriv, &identityPriv,
|
||||
&preKeyPriv, &device.SignedPreKey.KeyID, &preKeySig,
|
||||
&device.AdvSecretKey, &account.Details, &account.AccountSignature, &account.DeviceSignature,
|
||||
&device.Platform, &device.BusinessName, &device.PushName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to scan session: %w", err)
|
||||
} else if len(noisePriv) != 32 || len(identityPriv) != 32 || len(preKeyPriv) != 32 || len(preKeySig) != 64 {
|
||||
return nil, ErrInvalidLength
|
||||
}
|
||||
|
||||
device.NoiseKey = keys.NewKeyPairFromPrivateKey(*(*[32]byte)(noisePriv))
|
||||
device.IdentityKey = keys.NewKeyPairFromPrivateKey(*(*[32]byte)(identityPriv))
|
||||
device.SignedPreKey.KeyPair = *keys.NewKeyPairFromPrivateKey(*(*[32]byte)(preKeyPriv))
|
||||
device.SignedPreKey.Signature = (*[64]byte)(preKeySig)
|
||||
device.Account = &account
|
||||
|
||||
innerStore := NewSQLStore(c, *device.ID)
|
||||
device.Identities = innerStore
|
||||
device.Sessions = innerStore
|
||||
device.PreKeys = innerStore
|
||||
device.SenderKeys = innerStore
|
||||
device.AppStateKeys = innerStore
|
||||
device.AppState = innerStore
|
||||
device.Contacts = innerStore
|
||||
device.ChatSettings = innerStore
|
||||
device.Container = c
|
||||
device.Initialized = true
|
||||
|
||||
return &device, nil
|
||||
}
|
||||
|
||||
// GetAllDevices finds all the devices in the database.
|
||||
func (c *Container) GetAllDevices() ([]*store.Device, error) {
|
||||
res, err := c.db.Query(getAllDevicesQuery)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query sessions: %w", err)
|
||||
}
|
||||
sessions := make([]*store.Device, 0)
|
||||
for res.Next() {
|
||||
sess, scanErr := c.scanDevice(res)
|
||||
if scanErr != nil {
|
||||
return sessions, scanErr
|
||||
}
|
||||
sessions = append(sessions, sess)
|
||||
}
|
||||
return sessions, nil
|
||||
}
|
||||
|
||||
// GetFirstDevice is a convenience method for getting the first device in the store. If there are
|
||||
// no devices, then a new device will be created. You should only use this if you don't want to
|
||||
// have multiple sessions simultaneously.
|
||||
func (c *Container) GetFirstDevice() (*store.Device, error) {
|
||||
devices, err := c.GetAllDevices()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(devices) == 0 {
|
||||
return c.NewDevice(), nil
|
||||
} else {
|
||||
return devices[0], nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetDevice finds the device with the specified JID in the database.
|
||||
//
|
||||
// If the device is not found, nil is returned instead.
|
||||
//
|
||||
// Note that the parameter usually must be an AD-JID.
|
||||
func (c *Container) GetDevice(jid types.JID) (*store.Device, error) {
|
||||
sess, err := c.scanDevice(c.db.QueryRow(getDeviceQuery, jid))
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return sess, err
|
||||
}
|
||||
|
||||
const (
|
||||
insertDeviceQuery = `
|
||||
INSERT INTO whatsmeow_device (jid, registration_id, noise_key, identity_key,
|
||||
signed_pre_key, signed_pre_key_id, signed_pre_key_sig,
|
||||
adv_key, adv_details, adv_account_sig, adv_device_sig,
|
||||
platform, business_name, push_name)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)
|
||||
ON CONFLICT (jid) DO UPDATE SET platform=$12, business_name=$13, push_name=$14
|
||||
`
|
||||
deleteDeviceQuery = `DELETE FROM whatsmeow_device WHERE jid=$1`
|
||||
)
|
||||
|
||||
// NewDevice creates a new device in this database.
|
||||
//
|
||||
// No data is actually stored before Save is called. However, the pairing process will automatically
|
||||
// call Save after a successful pairing, so you most likely don't need to call it yourself.
|
||||
func (c *Container) NewDevice() *store.Device {
|
||||
device := &store.Device{
|
||||
Log: c.log,
|
||||
Container: c,
|
||||
|
||||
NoiseKey: keys.NewKeyPair(),
|
||||
IdentityKey: keys.NewKeyPair(),
|
||||
RegistrationID: mathRand.Uint32(),
|
||||
AdvSecretKey: make([]byte, 32),
|
||||
}
|
||||
_, err := rand.Read(device.AdvSecretKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
device.SignedPreKey = device.IdentityKey.CreateSignedPreKey(1)
|
||||
return device
|
||||
}
|
||||
|
||||
// ErrDeviceIDMustBeSet is the error returned by PutDevice if you try to save a device before knowing its JID.
|
||||
var ErrDeviceIDMustBeSet = errors.New("device JID must be known before accessing database")
|
||||
|
||||
// PutDevice stores the given device in this database. This should be called through Device.Save()
|
||||
// (which usually doesn't need to be called manually, as the library does that automatically when relevant).
|
||||
func (c *Container) PutDevice(device *store.Device) error {
|
||||
if device.ID == nil {
|
||||
return ErrDeviceIDMustBeSet
|
||||
}
|
||||
_, err := c.db.Exec(insertDeviceQuery,
|
||||
device.ID.String(), device.RegistrationID, device.NoiseKey.Priv[:], device.IdentityKey.Priv[:],
|
||||
device.SignedPreKey.Priv[:], device.SignedPreKey.KeyID, device.SignedPreKey.Signature[:],
|
||||
device.AdvSecretKey, device.Account.Details, device.Account.AccountSignature, device.Account.DeviceSignature,
|
||||
device.Platform, device.BusinessName, device.PushName)
|
||||
|
||||
if !device.Initialized {
|
||||
innerStore := NewSQLStore(c, *device.ID)
|
||||
device.Identities = innerStore
|
||||
device.Sessions = innerStore
|
||||
device.PreKeys = innerStore
|
||||
device.SenderKeys = innerStore
|
||||
device.AppStateKeys = innerStore
|
||||
device.AppState = innerStore
|
||||
device.Contacts = innerStore
|
||||
device.ChatSettings = innerStore
|
||||
device.Initialized = true
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// DeleteDevice deletes the given device from this database. This should be called through Device.Delete()
|
||||
func (c *Container) DeleteDevice(store *store.Device) error {
|
||||
if store.ID == nil {
|
||||
return ErrDeviceIDMustBeSet
|
||||
}
|
||||
_, err := c.db.Exec(deleteDeviceQuery, store.ID.String())
|
||||
return err
|
||||
}
|
610
vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go
vendored
Normal file
610
vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go
vendored
Normal file
@ -0,0 +1,610 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package sqlstore contains an SQL-backed implementation of the interfaces in the store package.
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/whatsmeow/store"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/util/keys"
|
||||
)
|
||||
|
||||
// ErrInvalidLength is returned by some database getters if the database returned a byte array with an unexpected length.
|
||||
// This should be impossible, as the database schema contains CHECK()s for all the relevant columns.
|
||||
var ErrInvalidLength = errors.New("database returned byte array with illegal length")
|
||||
|
||||
// PostgresArrayWrapper is a function to wrap array values before passing them to the sql package.
|
||||
//
|
||||
// When using github.com/lib/pq, you should set
|
||||
// whatsmeow.PostgresArrayWrapper = pq.Array
|
||||
var PostgresArrayWrapper func(interface{}) interface {
|
||||
driver.Valuer
|
||||
sql.Scanner
|
||||
}
|
||||
|
||||
type SQLStore struct {
|
||||
*Container
|
||||
JID string
|
||||
|
||||
preKeyLock sync.Mutex
|
||||
|
||||
contactCache map[types.JID]*types.ContactInfo
|
||||
contactCacheLock sync.Mutex
|
||||
}
|
||||
|
||||
// NewSQLStore creates a new SQLStore with the given database container and user JID.
|
||||
// It contains implementations of all the different stores in the store package.
|
||||
//
|
||||
// In general, you should use Container.NewDevice or Container.GetDevice instead of this.
|
||||
func NewSQLStore(c *Container, jid types.JID) *SQLStore {
|
||||
return &SQLStore{
|
||||
Container: c,
|
||||
JID: jid.String(),
|
||||
contactCache: make(map[types.JID]*types.ContactInfo),
|
||||
}
|
||||
}
|
||||
|
||||
var _ store.IdentityStore = (*SQLStore)(nil)
|
||||
var _ store.SessionStore = (*SQLStore)(nil)
|
||||
var _ store.PreKeyStore = (*SQLStore)(nil)
|
||||
var _ store.SenderKeyStore = (*SQLStore)(nil)
|
||||
var _ store.AppStateSyncKeyStore = (*SQLStore)(nil)
|
||||
var _ store.AppStateStore = (*SQLStore)(nil)
|
||||
var _ store.ContactStore = (*SQLStore)(nil)
|
||||
|
||||
const (
|
||||
putIdentityQuery = `
|
||||
INSERT INTO whatsmeow_identity_keys (our_jid, their_id, identity) VALUES ($1, $2, $3)
|
||||
ON CONFLICT (our_jid, their_id) DO UPDATE SET identity=$3
|
||||
`
|
||||
deleteAllIdentitiesQuery = `DELETE FROM whatsmeow_identity_keys WHERE our_jid=$1 AND their_id LIKE $2`
|
||||
deleteIdentityQuery = `DELETE FROM whatsmeow_identity_keys WHERE our_jid=$1 AND their_id=$2`
|
||||
getIdentityQuery = `SELECT identity FROM whatsmeow_identity_keys WHERE our_jid=$1 AND their_id=$2`
|
||||
)
|
||||
|
||||
func (s *SQLStore) PutIdentity(address string, key [32]byte) error {
|
||||
_, err := s.db.Exec(putIdentityQuery, s.JID, address, key[:])
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) DeleteAllIdentities(phone string) error {
|
||||
_, err := s.db.Exec(deleteAllIdentitiesQuery, s.JID, phone+":%")
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) DeleteIdentity(address string) error {
|
||||
_, err := s.db.Exec(deleteAllIdentitiesQuery, s.JID, address)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) IsTrustedIdentity(address string, key [32]byte) (bool, error) {
|
||||
var existingIdentity []byte
|
||||
err := s.db.QueryRow(getIdentityQuery, s.JID, address).Scan(&existingIdentity)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// Trust if not known, it'll be saved automatically later
|
||||
return true, nil
|
||||
} else if err != nil {
|
||||
return false, err
|
||||
} else if len(existingIdentity) != 32 {
|
||||
return false, ErrInvalidLength
|
||||
}
|
||||
return *(*[32]byte)(existingIdentity) == key, nil
|
||||
}
|
||||
|
||||
const (
|
||||
getSessionQuery = `SELECT session FROM whatsmeow_sessions WHERE our_jid=$1 AND their_id=$2`
|
||||
hasSessionQuery = `SELECT true FROM whatsmeow_sessions WHERE our_jid=$1 AND their_id=$2`
|
||||
putSessionQuery = `
|
||||
INSERT INTO whatsmeow_sessions (our_jid, their_id, session) VALUES ($1, $2, $3)
|
||||
ON CONFLICT (our_jid, their_id) DO UPDATE SET session=$3
|
||||
`
|
||||
deleteAllSessionsQuery = `DELETE FROM whatsmeow_sessions WHERE our_jid=$1 AND their_id LIKE $2`
|
||||
deleteSessionQuery = `DELETE FROM whatsmeow_sessions WHERE our_jid=$1 AND their_id=$2`
|
||||
)
|
||||
|
||||
func (s *SQLStore) GetSession(address string) (session []byte, err error) {
|
||||
err = s.db.QueryRow(getSessionQuery, s.JID, address).Scan(&session)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SQLStore) HasSession(address string) (has bool, err error) {
|
||||
err = s.db.QueryRow(hasSessionQuery, s.JID, address).Scan(&has)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SQLStore) PutSession(address string, session []byte) error {
|
||||
_, err := s.db.Exec(putSessionQuery, s.JID, address, session)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) DeleteAllSessions(phone string) error {
|
||||
_, err := s.db.Exec(deleteAllSessionsQuery, s.JID, phone+":%")
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) DeleteSession(address string) error {
|
||||
_, err := s.db.Exec(deleteSessionQuery, s.JID, address)
|
||||
return err
|
||||
}
|
||||
|
||||
const (
|
||||
getLastPreKeyIDQuery = `SELECT MAX(key_id) FROM whatsmeow_pre_keys WHERE jid=$1`
|
||||
insertPreKeyQuery = `INSERT INTO whatsmeow_pre_keys (jid, key_id, key, uploaded) VALUES ($1, $2, $3, $4)`
|
||||
getUnuploadedPreKeysQuery = `SELECT key_id, key FROM whatsmeow_pre_keys WHERE jid=$1 AND uploaded=false ORDER BY key_id LIMIT $2`
|
||||
getPreKeyQuery = `SELECT key_id, key FROM whatsmeow_pre_keys WHERE jid=$1 AND key_id=$2`
|
||||
deletePreKeyQuery = `DELETE FROM whatsmeow_pre_keys WHERE jid=$1 AND key_id=$2`
|
||||
markPreKeysAsUploadedQuery = `UPDATE whatsmeow_pre_keys SET uploaded=true WHERE jid=$1 AND key_id<=$2`
|
||||
getUploadedPreKeyCountQuery = `SELECT COUNT(*) FROM whatsmeow_pre_keys WHERE jid=$1 AND uploaded=true`
|
||||
)
|
||||
|
||||
func (s *SQLStore) genOnePreKey(id uint32, markUploaded bool) (*keys.PreKey, error) {
|
||||
key := keys.NewPreKey(id)
|
||||
_, err := s.db.Exec(insertPreKeyQuery, s.JID, key.KeyID, key.Priv[:], markUploaded)
|
||||
return key, err
|
||||
}
|
||||
|
||||
func (s *SQLStore) getNextPreKeyID() (uint32, error) {
|
||||
var lastKeyID sql.NullInt32
|
||||
err := s.db.QueryRow(getLastPreKeyIDQuery, s.JID).Scan(&lastKeyID)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to query next prekey ID: %w", err)
|
||||
}
|
||||
return uint32(lastKeyID.Int32) + 1, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) GenOnePreKey() (*keys.PreKey, error) {
|
||||
s.preKeyLock.Lock()
|
||||
defer s.preKeyLock.Unlock()
|
||||
nextKeyID, err := s.getNextPreKeyID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.genOnePreKey(nextKeyID, true)
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetOrGenPreKeys(count uint32) ([]*keys.PreKey, error) {
|
||||
s.preKeyLock.Lock()
|
||||
defer s.preKeyLock.Unlock()
|
||||
|
||||
res, err := s.db.Query(getUnuploadedPreKeysQuery, s.JID, count)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to query existing prekeys: %w", err)
|
||||
}
|
||||
newKeys := make([]*keys.PreKey, count)
|
||||
var existingCount uint32
|
||||
for res.Next() {
|
||||
var key *keys.PreKey
|
||||
key, err = scanPreKey(res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if key != nil {
|
||||
newKeys[existingCount] = key
|
||||
existingCount++
|
||||
}
|
||||
}
|
||||
|
||||
if existingCount < uint32(len(newKeys)) {
|
||||
var nextKeyID uint32
|
||||
nextKeyID, err = s.getNextPreKeyID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := existingCount; i < count; i++ {
|
||||
newKeys[i], err = s.genOnePreKey(nextKeyID, false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate prekey: %w", err)
|
||||
}
|
||||
nextKeyID++
|
||||
}
|
||||
}
|
||||
|
||||
return newKeys, nil
|
||||
}
|
||||
|
||||
func scanPreKey(row scannable) (*keys.PreKey, error) {
|
||||
var priv []byte
|
||||
var id uint32
|
||||
err := row.Scan(&id, &priv)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, err
|
||||
} else if len(priv) != 32 {
|
||||
return nil, ErrInvalidLength
|
||||
}
|
||||
return &keys.PreKey{
|
||||
KeyPair: *keys.NewKeyPairFromPrivateKey(*(*[32]byte)(priv)),
|
||||
KeyID: id,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetPreKey(id uint32) (*keys.PreKey, error) {
|
||||
return scanPreKey(s.db.QueryRow(getPreKeyQuery, s.JID, id))
|
||||
}
|
||||
|
||||
func (s *SQLStore) RemovePreKey(id uint32) error {
|
||||
_, err := s.db.Exec(deletePreKeyQuery, s.JID, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) MarkPreKeysAsUploaded(upToID uint32) error {
|
||||
_, err := s.db.Exec(markPreKeysAsUploadedQuery, s.JID, upToID)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) UploadedPreKeyCount() (count int, err error) {
|
||||
err = s.db.QueryRow(getUploadedPreKeyCountQuery, s.JID).Scan(&count)
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
getSenderKeyQuery = `SELECT sender_key FROM whatsmeow_sender_keys WHERE our_jid=$1 AND chat_id=$2 AND sender_id=$3`
|
||||
putSenderKeyQuery = `
|
||||
INSERT INTO whatsmeow_sender_keys (our_jid, chat_id, sender_id, sender_key) VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (our_jid, chat_id, sender_id) DO UPDATE SET sender_key=$4
|
||||
`
|
||||
)
|
||||
|
||||
func (s *SQLStore) PutSenderKey(group, user string, session []byte) error {
|
||||
_, err := s.db.Exec(putSenderKeyQuery, s.JID, group, user, session)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetSenderKey(group, user string) (key []byte, err error) {
|
||||
err = s.db.QueryRow(getSenderKeyQuery, s.JID, group, user).Scan(&key)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
putAppStateSyncKeyQuery = `
|
||||
INSERT INTO whatsmeow_app_state_sync_keys (jid, key_id, key_data, timestamp, fingerprint) VALUES ($1, $2, $3, $4, $5)
|
||||
ON CONFLICT (jid, key_id) DO UPDATE SET key_data=$3, timestamp=$4, fingerprint=$5
|
||||
`
|
||||
getAppStateSyncKeyQuery = `SELECT key_data, timestamp, fingerprint FROM whatsmeow_app_state_sync_keys WHERE jid=$1 AND key_id=$2`
|
||||
)
|
||||
|
||||
func (s *SQLStore) PutAppStateSyncKey(id []byte, key store.AppStateSyncKey) error {
|
||||
_, err := s.db.Exec(putAppStateSyncKeyQuery, s.JID, id, key.Data, key.Timestamp, key.Fingerprint)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetAppStateSyncKey(id []byte) (*store.AppStateSyncKey, error) {
|
||||
var key store.AppStateSyncKey
|
||||
err := s.db.QueryRow(getAppStateSyncKeyQuery, s.JID, id).Scan(&key.Data, &key.Timestamp, &key.Fingerprint)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return &key, err
|
||||
}
|
||||
|
||||
const (
|
||||
putAppStateVersionQuery = `
|
||||
INSERT INTO whatsmeow_app_state_version (jid, name, version, hash) VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (jid, name) DO UPDATE SET version=$3, hash=$4
|
||||
`
|
||||
getAppStateVersionQuery = `SELECT version, hash FROM whatsmeow_app_state_version WHERE jid=$1 AND name=$2`
|
||||
deleteAppStateVersionQuery = `DELETE FROM whatsmeow_app_state_version WHERE jid=$1 AND name=$2`
|
||||
putAppStateMutationMACsQuery = `INSERT INTO whatsmeow_app_state_mutation_macs (jid, name, version, index_mac, value_mac) VALUES `
|
||||
deleteAppStateMutationMACsQueryPostgres = `DELETE FROM whatsmeow_app_state_mutation_macs WHERE jid=$1 AND name=$2 AND index_mac=ANY($3::bytea[])`
|
||||
deleteAppStateMutationMACsQueryGeneric = `DELETE FROM whatsmeow_app_state_mutation_macs WHERE jid=$1 AND name=$2 AND index_mac IN `
|
||||
getAppStateMutationMACQuery = `SELECT value_mac FROM whatsmeow_app_state_mutation_macs WHERE jid=$1 AND name=$2 AND index_mac=$3 ORDER BY version DESC LIMIT 1`
|
||||
)
|
||||
|
||||
func (s *SQLStore) PutAppStateVersion(name string, version uint64, hash [128]byte) error {
|
||||
_, err := s.db.Exec(putAppStateVersionQuery, s.JID, name, version, hash[:])
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetAppStateVersion(name string) (version uint64, hash [128]byte, err error) {
|
||||
var uncheckedHash []byte
|
||||
err = s.db.QueryRow(getAppStateVersionQuery, s.JID, name).Scan(&version, &uncheckedHash)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
// version will be 0 and hash will be an empty array, which is the correct initial state
|
||||
err = nil
|
||||
} else if err != nil {
|
||||
// There's an error, just return it
|
||||
} else if len(uncheckedHash) != 128 {
|
||||
// This shouldn't happen
|
||||
err = ErrInvalidLength
|
||||
} else {
|
||||
// No errors, convert hash slice to array
|
||||
hash = *(*[128]byte)(uncheckedHash)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SQLStore) DeleteAppStateVersion(name string) error {
|
||||
_, err := s.db.Exec(deleteAppStateVersionQuery, s.JID, name)
|
||||
return err
|
||||
}
|
||||
|
||||
type execable interface {
|
||||
Exec(query string, args ...interface{}) (sql.Result, error)
|
||||
}
|
||||
|
||||
func (s *SQLStore) putAppStateMutationMACs(tx execable, name string, version uint64, mutations []store.AppStateMutationMAC) error {
|
||||
values := make([]interface{}, 3+len(mutations)*2)
|
||||
queryParts := make([]string, len(mutations))
|
||||
values[0] = s.JID
|
||||
values[1] = name
|
||||
values[2] = version
|
||||
for i, mutation := range mutations {
|
||||
baseIndex := 3 + i*2
|
||||
values[baseIndex] = mutation.IndexMAC
|
||||
values[baseIndex+1] = mutation.ValueMAC
|
||||
if s.dialect == "sqlite3" {
|
||||
queryParts[i] = fmt.Sprintf("(?1, ?2, ?3, ?%d, ?%d)", baseIndex+1, baseIndex+2)
|
||||
} else {
|
||||
queryParts[i] = fmt.Sprintf("($1, $2, $3, $%d, $%d)", baseIndex+1, baseIndex+2)
|
||||
}
|
||||
}
|
||||
_, err := tx.Exec(putAppStateMutationMACsQuery+strings.Join(queryParts, ","), values...)
|
||||
return err
|
||||
}
|
||||
|
||||
const mutationBatchSize = 400
|
||||
|
||||
func (s *SQLStore) PutAppStateMutationMACs(name string, version uint64, mutations []store.AppStateMutationMAC) error {
|
||||
if len(mutations) > mutationBatchSize {
|
||||
tx, err := s.db.Begin()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to start transaction: %w", err)
|
||||
}
|
||||
for i := 0; i < len(mutations); i += mutationBatchSize {
|
||||
var mutationSlice []store.AppStateMutationMAC
|
||||
if len(mutations) > i+mutationBatchSize {
|
||||
mutationSlice = mutations[i : i+mutationBatchSize]
|
||||
} else {
|
||||
mutationSlice = mutations[i:]
|
||||
}
|
||||
err = s.putAppStateMutationMACs(tx, name, version, mutationSlice)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to commit transaction: %w", err)
|
||||
}
|
||||
return nil
|
||||
} else if len(mutations) > 0 {
|
||||
return s.putAppStateMutationMACs(s.db, name, version, mutations)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) DeleteAppStateMutationMACs(name string, indexMACs [][]byte) (err error) {
|
||||
if len(indexMACs) == 0 {
|
||||
return
|
||||
}
|
||||
if s.dialect == "postgres" && PostgresArrayWrapper != nil {
|
||||
_, err = s.db.Exec(deleteAppStateMutationMACsQueryPostgres, s.JID, name, PostgresArrayWrapper(indexMACs))
|
||||
} else {
|
||||
args := make([]interface{}, 2+len(indexMACs))
|
||||
args[0] = s.JID
|
||||
args[1] = name
|
||||
queryParts := make([]string, len(indexMACs))
|
||||
for i, item := range indexMACs {
|
||||
args[2+i] = item
|
||||
queryParts[i] = fmt.Sprintf("$%d", i+3)
|
||||
}
|
||||
_, err = s.db.Exec(deleteAppStateMutationMACsQueryGeneric+"("+strings.Join(queryParts, ",")+")", args...)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetAppStateMutationMAC(name string, indexMAC []byte) (valueMAC []byte, err error) {
|
||||
err = s.db.QueryRow(getAppStateMutationMACQuery, s.JID, name, indexMAC).Scan(&valueMAC)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
putContactNameQuery = `
|
||||
INSERT INTO whatsmeow_contacts (our_jid, their_jid, first_name, full_name) VALUES ($1, $2, $3, $4)
|
||||
ON CONFLICT (our_jid, their_jid) DO UPDATE SET first_name=$3, full_name=$4
|
||||
`
|
||||
putPushNameQuery = `
|
||||
INSERT INTO whatsmeow_contacts (our_jid, their_jid, push_name) VALUES ($1, $2, $3)
|
||||
ON CONFLICT (our_jid, their_jid) DO UPDATE SET push_name=$3
|
||||
`
|
||||
putBusinessNameQuery = `
|
||||
INSERT INTO whatsmeow_contacts (our_jid, their_jid, business_name) VALUES ($1, $2, $3)
|
||||
ON CONFLICT (our_jid, their_jid) DO UPDATE SET business_name=$3
|
||||
`
|
||||
getContactQuery = `
|
||||
SELECT first_name, full_name, push_name, business_name FROM whatsmeow_contacts WHERE our_jid=$1 AND their_jid=$2
|
||||
`
|
||||
getAllContactsQuery = `
|
||||
SELECT their_jid, first_name, full_name, push_name, business_name FROM whatsmeow_contacts WHERE our_jid=$1
|
||||
`
|
||||
)
|
||||
|
||||
func (s *SQLStore) PutPushName(user types.JID, pushName string) (bool, string, error) {
|
||||
s.contactCacheLock.Lock()
|
||||
defer s.contactCacheLock.Unlock()
|
||||
|
||||
cached, err := s.getContact(user)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if cached.PushName != pushName {
|
||||
_, err = s.db.Exec(putPushNameQuery, s.JID, user, pushName)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
previousName := cached.PushName
|
||||
cached.PushName = pushName
|
||||
cached.Found = true
|
||||
return true, previousName, nil
|
||||
}
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) PutBusinessName(user types.JID, businessName string) error {
|
||||
s.contactCacheLock.Lock()
|
||||
defer s.contactCacheLock.Unlock()
|
||||
|
||||
cached, err := s.getContact(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cached.BusinessName != businessName {
|
||||
_, err = s.db.Exec(putBusinessNameQuery, s.JID, user, businessName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cached.BusinessName = businessName
|
||||
cached.Found = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) PutContactName(user types.JID, firstName, fullName string) error {
|
||||
s.contactCacheLock.Lock()
|
||||
defer s.contactCacheLock.Unlock()
|
||||
|
||||
cached, err := s.getContact(user)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cached.FirstName != firstName || cached.FullName != fullName {
|
||||
_, err = s.db.Exec(putContactNameQuery, s.JID, user, firstName, fullName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cached.FirstName = firstName
|
||||
cached.FullName = fullName
|
||||
cached.Found = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) getContact(user types.JID) (*types.ContactInfo, error) {
|
||||
cached, ok := s.contactCache[user]
|
||||
if ok {
|
||||
return cached, nil
|
||||
}
|
||||
|
||||
var first, full, push, business sql.NullString
|
||||
err := s.db.QueryRow(getContactQuery, s.JID, user).Scan(&first, &full, &push, &business)
|
||||
if err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, err
|
||||
}
|
||||
info := &types.ContactInfo{
|
||||
Found: err == nil,
|
||||
FirstName: first.String,
|
||||
FullName: full.String,
|
||||
PushName: push.String,
|
||||
BusinessName: business.String,
|
||||
}
|
||||
s.contactCache[user] = info
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetContact(user types.JID) (types.ContactInfo, error) {
|
||||
s.contactCacheLock.Lock()
|
||||
info, err := s.getContact(user)
|
||||
s.contactCacheLock.Unlock()
|
||||
if err != nil {
|
||||
return types.ContactInfo{}, err
|
||||
}
|
||||
return *info, nil
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetAllContacts() (map[types.JID]types.ContactInfo, error) {
|
||||
s.contactCacheLock.Lock()
|
||||
defer s.contactCacheLock.Unlock()
|
||||
rows, err := s.db.Query(getAllContactsQuery, s.JID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
output := make(map[types.JID]types.ContactInfo, len(s.contactCache))
|
||||
for rows.Next() {
|
||||
var jid types.JID
|
||||
var first, full, push, business sql.NullString
|
||||
err = rows.Scan(&jid, &first, &full, &push, &business)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error scanning row: %w", err)
|
||||
}
|
||||
info := types.ContactInfo{
|
||||
Found: true,
|
||||
FirstName: first.String,
|
||||
FullName: full.String,
|
||||
PushName: push.String,
|
||||
BusinessName: business.String,
|
||||
}
|
||||
output[jid] = info
|
||||
s.contactCache[jid] = &info
|
||||
}
|
||||
return output, nil
|
||||
}
|
||||
|
||||
const (
|
||||
putChatSettingQuery = `
|
||||
INSERT INTO whatsmeow_chat_settings (our_jid, chat_jid, %[1]s) VALUES ($1, $2, $3)
|
||||
ON CONFLICT (our_jid, chat_jid) DO UPDATE SET %[1]s=$3
|
||||
`
|
||||
getChatSettingsQuery = `
|
||||
SELECT muted_until, pinned, archived FROM whatsmeow_chat_settings WHERE our_jid=$1 AND chat_jid=$2
|
||||
`
|
||||
)
|
||||
|
||||
func (s *SQLStore) PutMutedUntil(chat types.JID, mutedUntil time.Time) error {
|
||||
var val int64
|
||||
if !mutedUntil.IsZero() {
|
||||
val = mutedUntil.Unix()
|
||||
}
|
||||
_, err := s.db.Exec(fmt.Sprintf(putChatSettingQuery, "muted_until"), s.JID, chat, val)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) PutPinned(chat types.JID, pinned bool) error {
|
||||
_, err := s.db.Exec(fmt.Sprintf(putChatSettingQuery, "pinned"), s.JID, chat, pinned)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) PutArchived(chat types.JID, archived bool) error {
|
||||
_, err := s.db.Exec(fmt.Sprintf(putChatSettingQuery, "archived"), s.JID, chat, archived)
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *SQLStore) GetChatSettings(chat types.JID) (settings types.LocalChatSettings, err error) {
|
||||
var mutedUntil int64
|
||||
err = s.db.QueryRow(getChatSettingsQuery, s.JID, chat).Scan(&mutedUntil, &settings.Pinned, &settings.Archived)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
err = nil
|
||||
} else if err != nil {
|
||||
return
|
||||
} else {
|
||||
settings.Found = true
|
||||
}
|
||||
if mutedUntil != 0 {
|
||||
settings.MutedUntil = time.Unix(mutedUntil, 0)
|
||||
}
|
||||
return
|
||||
}
|
214
vendor/go.mau.fi/whatsmeow/store/sqlstore/upgrade.go
vendored
Normal file
214
vendor/go.mau.fi/whatsmeow/store/sqlstore/upgrade.go
vendored
Normal file
@ -0,0 +1,214 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type upgradeFunc func(*sql.Tx, *Container) error
|
||||
|
||||
// Upgrades is a list of functions that will upgrade a database to the latest version.
|
||||
//
|
||||
// This may be of use if you want to manage the database fully manually, but in most cases you
|
||||
// should just call Container.Upgrade to let the library handle everything.
|
||||
var Upgrades = [...]upgradeFunc{upgradeV1}
|
||||
|
||||
func (c *Container) getVersion() (int, error) {
|
||||
_, err := c.db.Exec("CREATE TABLE IF NOT EXISTS whatsmeow_version (version INTEGER)")
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
|
||||
version := 0
|
||||
row := c.db.QueryRow("SELECT version FROM whatsmeow_version LIMIT 1")
|
||||
if row != nil {
|
||||
_ = row.Scan(&version)
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
func (c *Container) setVersion(tx *sql.Tx, version int) error {
|
||||
_, err := tx.Exec("DELETE FROM whatsmeow_version")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec("INSERT INTO whatsmeow_version (version) VALUES ($1)", version)
|
||||
return err
|
||||
}
|
||||
|
||||
// Upgrade upgrades the database from the current to the latest version available.
|
||||
func (c *Container) Upgrade() error {
|
||||
version, err := c.getVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for ; version < len(Upgrades); version++ {
|
||||
var tx *sql.Tx
|
||||
tx, err = c.db.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
migrateFunc := Upgrades[version]
|
||||
err = migrateFunc(tx, c)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
if err = c.setVersion(tx, version+1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = tx.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func upgradeV1(tx *sql.Tx, _ *Container) error {
|
||||
_, err := tx.Exec(`CREATE TABLE whatsmeow_device (
|
||||
jid TEXT PRIMARY KEY,
|
||||
|
||||
registration_id BIGINT NOT NULL CHECK ( registration_id >= 0 AND registration_id < 4294967296 ),
|
||||
|
||||
noise_key bytea NOT NULL CHECK ( length(noise_key) = 32 ),
|
||||
identity_key bytea NOT NULL CHECK ( length(identity_key) = 32 ),
|
||||
|
||||
signed_pre_key bytea NOT NULL CHECK ( length(signed_pre_key) = 32 ),
|
||||
signed_pre_key_id INTEGER NOT NULL CHECK ( signed_pre_key_id >= 0 AND signed_pre_key_id < 16777216 ),
|
||||
signed_pre_key_sig bytea NOT NULL CHECK ( length(signed_pre_key_sig) = 64 ),
|
||||
|
||||
adv_key bytea NOT NULL,
|
||||
adv_details bytea NOT NULL,
|
||||
adv_account_sig bytea NOT NULL CHECK ( length(adv_account_sig) = 64 ),
|
||||
adv_device_sig bytea NOT NULL CHECK ( length(adv_device_sig) = 64 ),
|
||||
|
||||
platform TEXT NOT NULL DEFAULT '',
|
||||
business_name TEXT NOT NULL DEFAULT '',
|
||||
push_name TEXT NOT NULL DEFAULT ''
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_identity_keys (
|
||||
our_jid TEXT,
|
||||
their_id TEXT,
|
||||
identity bytea NOT NULL CHECK ( length(identity) = 32 ),
|
||||
|
||||
PRIMARY KEY (our_jid, their_id),
|
||||
FOREIGN KEY (our_jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_pre_keys (
|
||||
jid TEXT,
|
||||
key_id INTEGER CHECK ( key_id >= 0 AND key_id < 16777216 ),
|
||||
key bytea NOT NULL CHECK ( length(key) = 32 ),
|
||||
uploaded BOOLEAN NOT NULL,
|
||||
|
||||
PRIMARY KEY (jid, key_id),
|
||||
FOREIGN KEY (jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_sessions (
|
||||
our_jid TEXT,
|
||||
their_id TEXT,
|
||||
session bytea,
|
||||
|
||||
PRIMARY KEY (our_jid, their_id),
|
||||
FOREIGN KEY (our_jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_sender_keys (
|
||||
our_jid TEXT,
|
||||
chat_id TEXT,
|
||||
sender_id TEXT,
|
||||
sender_key bytea NOT NULL,
|
||||
|
||||
PRIMARY KEY (our_jid, chat_id, sender_id),
|
||||
FOREIGN KEY (our_jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_app_state_sync_keys (
|
||||
jid TEXT,
|
||||
key_id bytea,
|
||||
key_data bytea NOT NULL,
|
||||
timestamp BIGINT NOT NULL,
|
||||
fingerprint bytea NOT NULL,
|
||||
|
||||
PRIMARY KEY (jid, key_id),
|
||||
FOREIGN KEY (jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_app_state_version (
|
||||
jid TEXT,
|
||||
name TEXT,
|
||||
version BIGINT NOT NULL,
|
||||
hash bytea NOT NULL CHECK ( length(hash) = 128 ),
|
||||
|
||||
PRIMARY KEY (jid, name),
|
||||
FOREIGN KEY (jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_app_state_mutation_macs (
|
||||
jid TEXT,
|
||||
name TEXT,
|
||||
version BIGINT,
|
||||
index_mac bytea CHECK ( length(index_mac) = 32 ),
|
||||
value_mac bytea NOT NULL CHECK ( length(value_mac) = 32 ),
|
||||
|
||||
PRIMARY KEY (jid, name, version, index_mac),
|
||||
FOREIGN KEY (jid, name) REFERENCES whatsmeow_app_state_version(jid, name) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_contacts (
|
||||
our_jid TEXT,
|
||||
their_jid TEXT,
|
||||
first_name TEXT,
|
||||
full_name TEXT,
|
||||
push_name TEXT,
|
||||
business_name TEXT,
|
||||
|
||||
PRIMARY KEY (our_jid, their_jid),
|
||||
FOREIGN KEY (our_jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = tx.Exec(`CREATE TABLE whatsmeow_chat_settings (
|
||||
our_jid TEXT,
|
||||
chat_jid TEXT,
|
||||
muted_until BIGINT NOT NULL DEFAULT 0,
|
||||
pinned BOOLEAN NOT NULL DEFAULT false,
|
||||
archived BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
PRIMARY KEY (our_jid, chat_jid),
|
||||
FOREIGN KEY (our_jid) REFERENCES whatsmeow_device(jid) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
)`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
Reference in New Issue
Block a user