diff options
Diffstat (limited to 'vendor/go.mau.fi/whatsmeow/appstate.go')
-rw-r--r-- | vendor/go.mau.fi/whatsmeow/appstate.go | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/vendor/go.mau.fi/whatsmeow/appstate.go b/vendor/go.mau.fi/whatsmeow/appstate.go new file mode 100644 index 00000000..9a4c6c00 --- /dev/null +++ b/vendor/go.mau.fi/whatsmeow/appstate.go @@ -0,0 +1,193 @@ +// 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 whatsmeow + +import ( + "fmt" + "time" + + "go.mau.fi/whatsmeow/appstate" + waBinary "go.mau.fi/whatsmeow/binary" + waProto "go.mau.fi/whatsmeow/binary/proto" + "go.mau.fi/whatsmeow/types" + "go.mau.fi/whatsmeow/types/events" +) + +// FetchAppState fetches updates to the given type of app state. If fullSync is true, the current +// cached state will be removed and all app state patches will be re-fetched from the server. +func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotSynced bool) error { + cli.appStateSyncLock.Lock() + defer cli.appStateSyncLock.Unlock() + if fullSync { + err := cli.Store.AppState.DeleteAppStateVersion(string(name)) + if err != nil { + return fmt.Errorf("failed to reset app state %s version: %w", name, err) + } + } + version, hash, err := cli.Store.AppState.GetAppStateVersion(string(name)) + if err != nil { + return fmt.Errorf("failed to get app state %s version: %w", name, err) + } + if version == 0 { + fullSync = true + } else if onlyIfNotSynced { + return nil + } + + state := appstate.HashState{Version: version, Hash: hash} + + hasMore := true + wantSnapshot := fullSync + for hasMore { + patches, err := cli.fetchAppStatePatches(name, state.Version, wantSnapshot) + wantSnapshot = false + if err != nil { + return fmt.Errorf("failed to fetch app state %s patches: %w", name, err) + } + hasMore = patches.HasMorePatches + + mutations, newState, err := cli.appStateProc.DecodePatches(patches, state, true) + if err != nil { + return fmt.Errorf("failed to decode app state %s patches: %w", name, err) + } + state = newState + for _, mutation := range mutations { + cli.dispatchAppState(mutation, !fullSync || cli.EmitAppStateEventsOnFullSync) + } + } + if fullSync { + cli.Log.Debugf("Full sync of app state %s completed. Current version: %d", name, state.Version) + cli.dispatchEvent(&events.AppStateSyncComplete{Name: name}) + } else { + cli.Log.Debugf("Synced app state %s from version %d to %d", name, version, state.Version) + } + return nil +} + +func (cli *Client) dispatchAppState(mutation appstate.Mutation, dispatchEvts bool) { + if mutation.Operation != waProto.SyncdMutation_SET { + return + } + + if dispatchEvts { + cli.dispatchEvent(&events.AppState{Index: mutation.Index, SyncActionValue: mutation.Action}) + } + + var jid types.JID + if len(mutation.Index) > 1 { + jid, _ = types.ParseJID(mutation.Index[1]) + } + ts := time.Unix(mutation.Action.GetTimestamp(), 0) + + var storeUpdateError error + var eventToDispatch interface{} + switch mutation.Index[0] { + case "mute": + act := mutation.Action.GetMuteAction() + eventToDispatch = &events.Mute{JID: jid, Timestamp: ts, Action: act} + var mutedUntil time.Time + if act.GetMuted() { + mutedUntil = time.Unix(act.GetMuteEndTimestamp(), 0) + } + if cli.Store.ChatSettings != nil { + storeUpdateError = cli.Store.ChatSettings.PutMutedUntil(jid, mutedUntil) + } + case "pin_v1": + act := mutation.Action.GetPinAction() + eventToDispatch = &events.Pin{JID: jid, Timestamp: ts, Action: act} + if cli.Store.ChatSettings != nil { + storeUpdateError = cli.Store.ChatSettings.PutPinned(jid, act.GetPinned()) + } + case "archive": + act := mutation.Action.GetArchiveChatAction() + eventToDispatch = &events.Archive{JID: jid, Timestamp: ts, Action: act} + if cli.Store.ChatSettings != nil { + storeUpdateError = cli.Store.ChatSettings.PutArchived(jid, act.GetArchived()) + } + case "contact": + act := mutation.Action.GetContactAction() + eventToDispatch = &events.Contact{JID: jid, Timestamp: ts, Action: act} + if cli.Store.Contacts != nil { + storeUpdateError = cli.Store.Contacts.PutContactName(jid, act.GetFirstName(), act.GetFullName()) + } + case "star": + if len(mutation.Index) < 5 { + return + } + evt := events.Star{ + ChatJID: jid, + MessageID: mutation.Index[2], + Timestamp: ts, + Action: mutation.Action.GetStarAction(), + IsFromMe: mutation.Index[3] == "1", + } + if mutation.Index[4] != "0" { + evt.SenderJID, _ = types.ParseJID(mutation.Index[4]) + } + eventToDispatch = &evt + case "deleteMessageForMe": + if len(mutation.Index) < 5 { + return + } + evt := events.DeleteForMe{ + ChatJID: jid, + MessageID: mutation.Index[2], + Timestamp: ts, + Action: mutation.Action.GetDeleteMessageForMeAction(), + IsFromMe: mutation.Index[3] == "1", + } + if mutation.Index[4] != "0" { + evt.SenderJID, _ = types.ParseJID(mutation.Index[4]) + } + eventToDispatch = &evt + case "setting_pushName": + eventToDispatch = &events.PushNameSetting{Timestamp: ts, Action: mutation.Action.GetPushNameSetting()} + cli.Store.PushName = mutation.Action.GetPushNameSetting().GetName() + err := cli.Store.Save() + if err != nil { + cli.Log.Errorf("Failed to save device store after updating push name: %v", err) + } + case "setting_unarchiveChats": + eventToDispatch = &events.UnarchiveChatsSetting{Timestamp: ts, Action: mutation.Action.GetUnarchiveChatsSetting()} + } + if storeUpdateError != nil { + cli.Log.Errorf("Failed to update device store after app state mutation: %v", storeUpdateError) + } + if dispatchEvts && eventToDispatch != nil { + cli.dispatchEvent(eventToDispatch) + } +} + +func (cli *Client) downloadExternalAppStateBlob(ref *waProto.ExternalBlobReference) ([]byte, error) { + return cli.Download(ref) +} + +func (cli *Client) fetchAppStatePatches(name appstate.WAPatchName, fromVersion uint64, snapshot bool) (*appstate.PatchList, error) { + attrs := waBinary.Attrs{ + "name": string(name), + "return_snapshot": snapshot, + } + if !snapshot { + attrs["version"] = fromVersion + } + resp, err := cli.sendIQ(infoQuery{ + Namespace: "w:sync:app:state", + Type: "set", + To: types.ServerJID, + Content: []waBinary.Node{{ + Tag: "sync", + Content: []waBinary.Node{{ + Tag: "collection", + Attrs: attrs, + }}, + }}, + }) + if err != nil { + return nil, err + } + return appstate.ParsePatchList(resp, cli.downloadExternalAppStateBlob) +} |