diff options
Diffstat (limited to 'vendor/go.mau.fi/whatsmeow/store')
-rw-r--r-- | vendor/go.mau.fi/whatsmeow/store/clientpayload.go | 96 | ||||
-rw-r--r-- | vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go | 90 | ||||
-rw-r--r-- | vendor/go.mau.fi/whatsmeow/store/store.go | 9 |
3 files changed, 170 insertions, 25 deletions
diff --git a/vendor/go.mau.fi/whatsmeow/store/clientpayload.go b/vendor/go.mau.fi/whatsmeow/store/clientpayload.go index 81519ee2..675719a2 100644 --- a/vendor/go.mau.fi/whatsmeow/store/clientpayload.go +++ b/vendor/go.mau.fi/whatsmeow/store/clientpayload.go @@ -20,36 +20,98 @@ import ( waProto "go.mau.fi/whatsmeow/binary/proto" ) +// WAVersionContainer is a container for a WhatsApp web version number. +type WAVersionContainer [3]uint32 + +// ParseVersion parses a version string (three dot-separated numbers) into a WAVersionContainer. +func ParseVersion(version string) (parsed WAVersionContainer, err error) { + var part1, part2, part3 int + if parts := strings.Split(version, "."); len(parts) != 3 { + err = fmt.Errorf("'%s' doesn't contain three dot-separated parts", version) + } else if part1, err = strconv.Atoi(parts[0]); err != nil { + err = fmt.Errorf("first part of '%s' is not a number: %w", version, err) + } else if part2, err = strconv.Atoi(parts[1]); err != nil { + err = fmt.Errorf("second part of '%s' is not a number: %w", version, err) + } else if part3, err = strconv.Atoi(parts[2]); err != nil { + err = fmt.Errorf("third part of '%s' is not a number: %w", version, err) + } else { + parsed = WAVersionContainer{uint32(part1), uint32(part2), uint32(part3)} + } + return +} + +func (vc WAVersionContainer) LessThan(other WAVersionContainer) bool { + return vc[0] < other[0] || + (vc[0] == other[0] && vc[1] < other[1]) || + (vc[0] == other[0] && vc[1] == other[1] && vc[2] < other[2]) +} + +// IsZero returns true if the version is zero. +func (vc WAVersionContainer) IsZero() bool { + return vc == [3]uint32{0, 0, 0} +} + +// String returns the version number as a dot-separated string. +func (vc WAVersionContainer) String() string { + parts := make([]string, len(vc)) + for i, part := range vc { + parts[i] = strconv.Itoa(int(part)) + } + return strings.Join(parts, ".") +} + +// Hash returns the md5 hash of the String representation of this version. +func (vc WAVersionContainer) Hash() [16]byte { + return md5.Sum([]byte(vc.String())) +} + +func (vc WAVersionContainer) ProtoAppVersion() *waProto.AppVersion { + return &waProto.AppVersion{ + Primary: &vc[0], + Secondary: &vc[1], + Tertiary: &vc[2], + } +} + // waVersion is the WhatsApp web client version -var waVersion = []uint32{2, 2202, 9} +var waVersion = WAVersionContainer{2, 2208, 7} // waVersionHash is the md5 hash of a dot-separated waVersion var waVersionHash [16]byte func init() { - waVersionParts := make([]string, len(waVersion)) - for i, part := range waVersion { - waVersionParts[i] = strconv.Itoa(int(part)) + waVersionHash = waVersion.Hash() +} + +// GetWAVersion gets the current WhatsApp web client version. +func GetWAVersion() WAVersionContainer { + return waVersion +} + +// SetWAVersion sets the current WhatsApp web client version. +// +// In general, you should keep the library up-to-date instead of using this, +// as there may be code changes that are necessary too (like protobuf schema changes). +func SetWAVersion(version WAVersionContainer) { + if version.IsZero() { + return } - waVersionString := strings.Join(waVersionParts, ".") - waVersionHash = md5.Sum([]byte(waVersionString)) + waVersion = version + waVersionHash = version.Hash() } var BaseClientPayload = &waProto.ClientPayload{ UserAgent: &waProto.UserAgent{ Platform: waProto.UserAgent_WEB.Enum(), ReleaseChannel: waProto.UserAgent_RELEASE.Enum(), - AppVersion: &waProto.AppVersion{ - Primary: &waVersion[0], - Secondary: &waVersion[1], - Tertiary: &waVersion[2], - }, - Mcc: proto.String("000"), - Mnc: proto.String("000"), - OsVersion: proto.String("0.1.0"), - Manufacturer: proto.String(""), - Device: proto.String("Desktop"), - OsBuildNumber: proto.String("0.1.0"), + AppVersion: waVersion.ProtoAppVersion(), + Mcc: proto.String("000"), + Mnc: proto.String("000"), + OsVersion: proto.String("0.1.0"), + Manufacturer: proto.String(""), + Device: proto.String("Desktop"), + OsBuildNumber: proto.String("0.1.0"), + LocaleLanguageIso6391: proto.String("en"), LocaleCountryIso31661Alpha2: proto.String("en"), }, diff --git a/vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go b/vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go index ad1eea7e..01ec2056 100644 --- a/vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go +++ b/vendor/go.mau.fi/whatsmeow/store/sqlstore/store.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Tulir Asokan +// Copyright (c) 2022 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 @@ -349,15 +349,15 @@ func (s *SQLStore) putAppStateMutationMACs(tx execable, name string, version uin values[0] = s.JID values[1] = name values[2] = version + placeholderSyntax := "($1, $2, $3, $%d, $%d)" + if s.dialect == "sqlite3" { + placeholderSyntax = "(?1, ?2, ?3, ?%d, ?%d)" + } 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) - } + queryParts[i] = fmt.Sprintf(placeholderSyntax, baseIndex+1, baseIndex+2) } _, err := tx.Exec(putAppStateMutationMACsQuery+strings.Join(queryParts, ","), values...) return err @@ -426,7 +426,12 @@ func (s *SQLStore) GetAppStateMutationMAC(name string, indexMAC []byte) (valueMA 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 + ON CONFLICT (our_jid, their_jid) DO UPDATE SET first_name=excluded.first_name, full_name=excluded.full_name + ` + putManyContactNamesQuery = ` + INSERT INTO whatsmeow_contacts (our_jid, their_jid, first_name, full_name) + VALUES %s + ON CONFLICT (our_jid, their_jid) DO UPDATE SET first_name=excluded.first_name, full_name=excluded.full_name ` putPushNameQuery = ` INSERT INTO whatsmeow_contacts (our_jid, their_jid, push_name) VALUES ($1, $2, $3) @@ -504,6 +509,77 @@ func (s *SQLStore) PutContactName(user types.JID, firstName, fullName string) er return nil } +const contactBatchSize = 300 + +func (s *SQLStore) putContactNamesBatch(tx execable, contacts []store.ContactEntry) error { + values := make([]interface{}, 1, 1+len(contacts)*3) + queryParts := make([]string, 0, len(contacts)) + values[0] = s.JID + placeholderSyntax := "($1, $%d, $%d, $%d)" + if s.dialect == "sqlite3" { + placeholderSyntax = "(?1, ?%d, ?%d, ?%d)" + } + i := 0 + handledContacts := make(map[types.JID]struct{}, len(contacts)) + for _, contact := range contacts { + if contact.JID.IsEmpty() { + s.log.Warnf("Empty contact info in mass insert: %+v", contact) + continue + } + // The whole query will break if there are duplicates, so make sure there aren't any duplicates + _, alreadyHandled := handledContacts[contact.JID] + if alreadyHandled { + s.log.Warnf("Duplicate contact info for %s in mass insert", contact.JID) + continue + } + handledContacts[contact.JID] = struct{}{} + baseIndex := i*3 + 1 + values = append(values, contact.JID.String(), contact.FirstName, contact.FullName) + queryParts = append(queryParts, fmt.Sprintf(placeholderSyntax, baseIndex+1, baseIndex+2, baseIndex+3)) + i++ + } + _, err := tx.Exec(fmt.Sprintf(putManyContactNamesQuery, strings.Join(queryParts, ",")), values...) + return err +} + +func (s *SQLStore) PutAllContactNames(contacts []store.ContactEntry) error { + if len(contacts) > contactBatchSize { + tx, err := s.db.Begin() + if err != nil { + return fmt.Errorf("failed to start transaction: %w", err) + } + for i := 0; i < len(contacts); i += contactBatchSize { + var contactSlice []store.ContactEntry + if len(contacts) > i+contactBatchSize { + contactSlice = contacts[i : i+contactBatchSize] + } else { + contactSlice = contacts[i:] + } + err = s.putContactNamesBatch(tx, contactSlice) + if err != nil { + _ = tx.Rollback() + return err + } + } + err = tx.Commit() + if err != nil { + return fmt.Errorf("failed to commit transaction: %w", err) + } + } else if len(contacts) > 0 { + err := s.putContactNamesBatch(s.db, contacts) + if err != nil { + return err + } + } else { + return nil + } + s.contactCacheLock.Lock() + // Just clear the cache, fetching pushnames and business names would be too much effort + s.contactCache = make(map[types.JID]*types.ContactInfo) + s.contactCacheLock.Unlock() + return nil +} + func (s *SQLStore) getContact(user types.JID) (*types.ContactInfo, error) { cached, ok := s.contactCache[user] if ok { diff --git a/vendor/go.mau.fi/whatsmeow/store/store.go b/vendor/go.mau.fi/whatsmeow/store/store.go index 65624b9f..67fe38fa 100644 --- a/vendor/go.mau.fi/whatsmeow/store/store.go +++ b/vendor/go.mau.fi/whatsmeow/store/store.go @@ -1,4 +1,4 @@ -// Copyright (c) 2021 Tulir Asokan +// Copyright (c) 2022 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 @@ -71,10 +71,17 @@ type AppStateStore interface { GetAppStateMutationMAC(name string, indexMAC []byte) (valueMAC []byte, err error) } +type ContactEntry struct { + JID types.JID + FirstName string + FullName string +} + type ContactStore interface { PutPushName(user types.JID, pushName string) (bool, string, error) PutBusinessName(user types.JID, businessName string) error PutContactName(user types.JID, fullName, firstName string) error + PutAllContactNames(contacts []ContactEntry) error GetContact(user types.JID) (types.ContactInfo, error) GetAllContacts() (map[types.JID]types.ContactInfo, error) } |